├── .gitignore ├── src ├── main │ └── java │ │ └── org │ │ └── destiny │ │ ├── jvm │ │ ├── model │ │ │ ├── attr │ │ │ │ ├── StackMapTable.java │ │ │ │ ├── ConstantValue.java │ │ │ │ ├── LineNumberTable.java │ │ │ │ ├── LocalVariableTable.java │ │ │ │ └── Code.java │ │ │ ├── constant │ │ │ │ ├── ClassIndex.java │ │ │ │ ├── ConstantInfo.java │ │ │ │ ├── detail │ │ │ │ │ ├── AbstractConstantInfo.java │ │ │ │ │ ├── ConstantStringInfo.java │ │ │ │ │ ├── ConstantClassInfo.java │ │ │ │ │ ├── ConstantUtf8Info.java │ │ │ │ │ ├── ConstantFieldRefInfo.java │ │ │ │ │ ├── ConstantMethodRefInfo.java │ │ │ │ │ └── ConstantNameAndTypeInfo.java │ │ │ │ ├── ConstantInfoEnum.java │ │ │ │ └── ConstantPool.java │ │ │ ├── AbstractAttributeInfo.java │ │ │ ├── runtime │ │ │ │ ├── StackFrame.java │ │ │ │ └── ExecutionResult.java │ │ │ ├── command │ │ │ │ ├── ByteCodeCmd.java │ │ │ │ ├── NoOperandCmd.java │ │ │ │ ├── OneOperandCmd.java │ │ │ │ ├── TwoOperandCmd.java │ │ │ │ ├── DupCmd.java │ │ │ │ ├── IloadCmd.java │ │ │ │ ├── Aload0Cmd.java │ │ │ │ ├── Aload1Cmd.java │ │ │ │ ├── Aload2Cmd.java │ │ │ │ ├── Fload3Cmd.java │ │ │ │ ├── Iload1Cmd.java │ │ │ │ ├── Iload2Cmd.java │ │ │ │ ├── Iload3Cmd.java │ │ │ │ ├── AStore1Cmd.java │ │ │ │ ├── VoidReturnCmd.java │ │ │ │ ├── BipushCmd.java │ │ │ │ ├── LdcCmd.java │ │ │ │ ├── GetFieldCmd.java │ │ │ │ ├── PutFieldCmd.java │ │ │ │ ├── NewObjectCmd.java │ │ │ │ ├── InvokeSpecialCmd.java │ │ │ │ ├── InvokeVirtualCmd.java │ │ │ │ ├── GetStaticFieldCmd.java │ │ │ │ └── CommandParser.java │ │ │ ├── InterfaceInfo.java │ │ │ ├── AccessFlagEnum.java │ │ │ ├── FieldInfo.java │ │ │ ├── MethodInfo.java │ │ │ ├── AccessFlag.java │ │ │ └── ClassFile.java │ │ ├── test │ │ │ ├── EmployeeV1.java │ │ │ └── Employee.java │ │ ├── MiniJVM.java │ │ ├── ExecutorEngine.java │ │ ├── util │ │ │ ├── CommandIterator.java │ │ │ ├── ConstantFactory.java │ │ │ ├── ByteCodeIterator.java │ │ │ └── AttributeFactory.java │ │ ├── MethodArea.java │ │ └── loader │ │ │ ├── ClassFileLoader.java │ │ │ └── ClassFileAnalyser.java │ │ ├── hotswap │ │ ├── HotSwapClassLoader.java │ │ ├── HackSystem.java │ │ ├── ByteUtils.java │ │ └── ClassModifier.java │ │ └── classplader │ │ └── MyClassLoader.java └── test │ └── java │ └── org │ └── destiny │ ├── jvm │ └── loader │ │ ├── ClassFileAnalyserTest.java │ │ └── ClassFileLoaderTest.java │ └── classplader │ └── MyClassLoaderTest.java ├── minijvm.iml ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | 26 | .idea -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/attr/StackMapTable.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.attr; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class StackMapTable { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/ClassIndex.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class ClassIndex { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/ConstantInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class ConstantInfo { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/AbstractAttributeInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public abstract class AbstractAttributeInfo { 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/test/EmployeeV1.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.test; 2 | 3 | public class EmployeeV1 { 4 | 5 | 6 | private String name; 7 | private int age; 8 | 9 | public EmployeeV1(String name, int age) { 10 | this.name = name; 11 | this.age = age; 12 | } 13 | 14 | public void setName(String name) { 15 | this.name = name; 16 | } 17 | public void setAge(int age){ 18 | this.age = age; 19 | } 20 | public void sayHello() { 21 | System.out.println("Hello , this is class Employee "); 22 | } 23 | public static void main(String[] args){ 24 | EmployeeV1 p = new EmployeeV1("Andy",29); 25 | p.sayHello(); 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/test/Employee.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.test; 2 | 3 | 4 | public class Employee { 5 | 6 | private String name; 7 | private int age; 8 | 9 | public Employee(String name, int age) { 10 | this.name = name; 11 | this.age = age; 12 | } 13 | 14 | public void sayHello(String name) { 15 | System.out.println( "hello " + name); 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | 22 | public void setAge(int age) { 23 | this.age = age; 24 | } 25 | 26 | public static void main(String[] args) { 27 | Employee employee = new Employee("destiny", 20); 28 | employee.sayHello("wangkang"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/runtime/StackFrame.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.runtime; 2 | 3 | import org.destiny.jvm.model.MethodInfo; /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 栈帧对象 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class StackFrame { 17 | public static StackFrame create(MethodInfo mainMethod) { 18 | return null; 19 | } 20 | 21 | public ExecutionResult execute() { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/runtime/ExecutionResult.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.runtime; 2 | 3 | import org.destiny.jvm.model.MethodInfo; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class ExecutionResult { 17 | private boolean pauseAndRunNewFrame; 18 | 19 | 20 | public boolean isPauseAndRunNewFrame() { 21 | return pauseAndRunNewFrame; 22 | } 23 | 24 | public MethodInfo getNextMethod() { 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/hotswap/HotSwapClassLoader.java: -------------------------------------------------------------------------------- 1 | package org.destiny.hotswap; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 为了多次载入执行类而加入的加载器 9 | * 把defineClass方法开放出来,只有外部显式调用的时候才会使用到loadByte方法 10 | * 由虚拟机调用的时候,仍然按照原有的双亲委派规则使用loadClass方法进行类加载 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class HotSwapClassLoader extends ClassLoader { 19 | 20 | public HotSwapClassLoader() { 21 | super(HotSwapClassLoader.class.getClassLoader()); 22 | } 23 | 24 | public Class loadByte(byte[] classByte) { 25 | return defineClass(null, classByte, 0, classByte.length); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/MiniJVM.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm; 2 | 3 | import org.destiny.jvm.loader.ClassFileLoader; 4 | 5 | /** 6 | * @author 王康 7 | * hzwangkang1@corp.netease.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2018 Netease, Inc. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class MiniJVM { 17 | 18 | public void run(String[] classPaths, String className) { 19 | ClassFileLoader loader = new ClassFileLoader(); 20 | 21 | // 添加 classPath 22 | for (String classPath : classPaths) { 23 | loader.addClassPath(classPath); 24 | } 25 | 26 | MethodArea methodArea = MethodArea.getInstance(); 27 | methodArea. 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/ByteCodeCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.runtime.ExecutionResult; 4 | import org.destiny.jvm.model.runtime.StackFrame; 5 | 6 | /** 7 | * @author 王康 8 | * destinywk@163.com 9 | * ------------------------------------------------------------------ 10 | *

11 | * ------------------------------------------------------------------ 12 | * Corpright 2017 Destiny, Org. All rights reserved. 13 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 14 | * @version JDK 1.8.0_101 15 | * @since 2017/8/22 16:38 16 | */ 17 | public abstract class ByteCodeCmd { 18 | 19 | private int offset; 20 | 21 | public int getOffset() { 22 | return offset; 23 | } 24 | 25 | public void setOffset(int offset) { 26 | this.offset = offset; 27 | } 28 | 29 | abstract void execute(StackFrame stackFrame, ExecutionResult result); 30 | 31 | abstract int getLength(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/InterfaceInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class InterfaceInfo { 17 | 18 | private List interfaces; 19 | 20 | public List getInterfaces() { 21 | return interfaces; 22 | } 23 | 24 | public void setInterfaces(List interfaces) { 25 | this.interfaces = interfaces; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "InterfaceInfo{" + 31 | "interfaces=" + interfaces + 32 | '}'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/AbstractConstantInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * 常量池中元素的顶层抽象类 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public abstract class AbstractConstantInfo { 19 | 20 | protected ConstantPool constantPool; 21 | 22 | // abstract protected String getContent(); 23 | 24 | public ConstantPool getConstantPool() { 25 | return constantPool; 26 | } 27 | 28 | public void setConstantPool(ConstantPool constantPool) { 29 | this.constantPool = constantPool; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/NoOperandCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public abstract class NoOperandCmd extends ByteCodeCmd { 17 | 18 | private ConstantPool constantPool; 19 | 20 | public NoOperandCmd(ConstantPool constantPool) { 21 | this.constantPool = constantPool; 22 | } 23 | 24 | public int getLength() { 25 | return 1; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "NoOperandCmd{" + 31 | "constantPool=" + constantPool + 32 | '}'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/OneOperandCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public abstract class OneOperandCmd extends ByteCodeCmd { 17 | 18 | private ConstantPool constantPool; 19 | 20 | public OneOperandCmd(ConstantPool constantPool) { 21 | this.constantPool = constantPool; 22 | } 23 | 24 | @Override 25 | public int getLength() { 26 | return 2; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "OneOperandCmd{" + 32 | "constantPool=" + constantPool + 33 | '}'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/TwoOperandCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public abstract class TwoOperandCmd extends ByteCodeCmd { 17 | 18 | private ConstantPool constantPool; 19 | 20 | 21 | 22 | public TwoOperandCmd(ConstantPool constantPool) { 23 | this.constantPool = constantPool; 24 | } 25 | 26 | @Override 27 | public int getLength() { 28 | return 3; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "TwoOperandCmd{" + 34 | "constantPool=" + constantPool + 35 | '}'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/hotswap/HackSystem.java: -------------------------------------------------------------------------------- 1 | package org.destiny.hotswap; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.InputStream; 5 | import java.io.PrintStream; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * 为JavaClass劫持java.lang.System提供支持 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2017 Destiny, Org. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2017/8/22 16:38 19 | */ 20 | public class HackSystem { 21 | 22 | public static final InputStream in = System.in; 23 | 24 | private static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 25 | 26 | public final static PrintStream out = new PrintStream(byteArrayOutputStream); 27 | 28 | public static final PrintStream err = out; 29 | 30 | public static String getBufferString() { 31 | return byteArrayOutputStream.toString(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/ConstantStringInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class ConstantStringInfo extends AbstractConstantInfo { 15 | 16 | private int tag = 8; 17 | private int stringIndex; 18 | 19 | public ConstantStringInfo() { 20 | } 21 | 22 | public ConstantStringInfo(int stringIndex) { 23 | this.stringIndex = stringIndex; 24 | } 25 | 26 | public int getStringIndex() { 27 | return stringIndex; 28 | } 29 | 30 | public void setStringIndex(int stringIndex) { 31 | this.stringIndex = stringIndex; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "ConstantStringInfo{" + 37 | "tag=" + tag + 38 | ", stringIndex=" + stringIndex + 39 | '}'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/ConstantClassInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 用于表示类或接口 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class ConstantClassInfo extends AbstractConstantInfo { 17 | 18 | private int tag = 7; 19 | private int nameIndex; 20 | 21 | public ConstantClassInfo() { 22 | } 23 | 24 | public ConstantClassInfo(int nameIndex) { 25 | this.nameIndex = nameIndex; 26 | } 27 | 28 | public int getNameIndex() { 29 | return nameIndex; 30 | } 31 | 32 | public void setNameIndex(int nameIndex) { 33 | this.nameIndex = nameIndex; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "ConstantClassInfo{" + 39 | "tag=" + tag + 40 | ", nameIndex=" + nameIndex + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/ExecutorEngine.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm; 2 | 3 | import org.destiny.jvm.model.MethodInfo; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | import java.util.Stack; 8 | 9 | /** 10 | * @author 王康 11 | * hzwangkang1@corp.netease.com 12 | * ------------------------------------------------------------------ 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2018 Netease, Inc. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2018/7/1 14:56 19 | */ 20 | public class ExecutorEngine { 21 | 22 | private Stack stack = new Stack<>(); 23 | 24 | public void execute(MethodInfo mainMethod) { 25 | StackFrame mainFrame = StackFrame.create(mainMethod); 26 | stack.push(mainFrame); 27 | 28 | while (!stack.empty()) { 29 | StackFrame stackFrame = stack.peek(); 30 | ExecutionResult result = stackFrame.execute(); 31 | 32 | if (result.isPauseAndRunNewFrame()) { 33 | MethodInfo nextFrame = result.getNextMethod(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/DupCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class DupCmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public DupCmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "DupCmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/IloadCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class IloadCmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public IloadCmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "IloadCmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Aload0Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Aload0Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Aload0Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Aload0Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Aload1Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Aload1Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Aload1Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Aload1Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Aload2Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Aload2Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Aload2Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Aload2Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Fload3Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Fload3Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Fload3Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Fload3Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Iload1Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Iload1Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Iload1Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Iload1Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Iload2Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Iload2Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Iload2Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Iload2Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/Iload3Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class Iload3Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public Iload3Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Iload3Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/AStore1Cmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class AStore1Cmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public AStore1Cmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "AStore1Cmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/VoidReturnCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class VoidReturnCmd extends NoOperandCmd { 19 | 20 | private String opCode; 21 | 22 | public VoidReturnCmd(ConstantPool constantPool, String opCode) { 23 | super(constantPool); 24 | this.opCode = opCode; 25 | } 26 | 27 | @Override 28 | public void execute(StackFrame stackFrame, ExecutionResult result) { 29 | 30 | } 31 | 32 | public String getOpCode() { 33 | return opCode; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "VoidReturnCmd{" + 39 | "opCode='" + opCode + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/util/CommandIterator.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.util; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 字节数组迭代器,自身维护一个字节数组的下标 9 | * 用于按照指定要求遍历字节数组,并返回不同格式数据 10 | *

11 | * ------------------------------------------------------------------ 12 | * Corpright 2017 Destiny, Org. All rights reserved. 13 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 14 | * @version JDK 1.8.0_101 15 | * @since 2017/8/22 16:38 16 | */ 17 | public class CommandIterator { 18 | 19 | private String codes; 20 | 21 | private int index; 22 | 23 | public CommandIterator(String codes) { 24 | this.codes = codes; 25 | index = 0; 26 | } 27 | 28 | public boolean hasNext() { 29 | return index < codes.length(); 30 | } 31 | 32 | public String next2CharAsString() { 33 | StringBuilder stringBuilder = new StringBuilder(); 34 | stringBuilder.append(codes.charAt(index++)); 35 | stringBuilder.append(codes.charAt(index++)); 36 | return stringBuilder.toString(); 37 | } 38 | 39 | public int next2CharAsInt() { 40 | String s = next2CharAsString(); 41 | return Integer.valueOf(s, 16); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /minijvm.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/BipushCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class BipushCmd extends OneOperandCmd { 19 | 20 | private String opCode; 21 | private int operand; 22 | 23 | public BipushCmd(ConstantPool constantPool, String opCode, int operand) { 24 | super(constantPool); 25 | this.opCode = opCode; 26 | this.operand = operand; 27 | } 28 | 29 | @Override 30 | public void execute(StackFrame stackFrame, ExecutionResult result) { 31 | 32 | } 33 | 34 | public String getOpCode() { 35 | return opCode; 36 | } 37 | 38 | public int getOperand() { 39 | return operand; 40 | } 41 | 42 | public void setOpCode(String opCode) { 43 | this.opCode = opCode; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/LdcCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class LdcCmd extends OneOperandCmd { 19 | 20 | private String opCode; 21 | private int operand; 22 | 23 | public LdcCmd(ConstantPool constantPool, String opCode, int operand) { 24 | super(constantPool); 25 | this.opCode = opCode; 26 | this.operand = operand; 27 | } 28 | 29 | @Override 30 | public void execute(StackFrame stackFrame, ExecutionResult result) { 31 | 32 | } 33 | 34 | public String getOpCode() { 35 | return opCode; 36 | } 37 | 38 | public int getOperand() { 39 | return operand; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "LdcCmd{" + 45 | "opCode='" + opCode + '\'' + 46 | ", operand=" + operand + 47 | '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/ConstantUtf8Info.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * 用于表示字符常量的值 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class ConstantUtf8Info extends AbstractConstantInfo { 19 | 20 | private int tag = 1; 21 | private int length; 22 | private byte[] bytes; 23 | 24 | public ConstantUtf8Info() { 25 | } 26 | 27 | public ConstantUtf8Info(int length, byte[] bytes) { 28 | this.length = length; 29 | this.bytes = bytes; 30 | } 31 | 32 | public int getLength() { 33 | return length; 34 | } 35 | 36 | public void setLength(int length) { 37 | this.length = length; 38 | } 39 | 40 | public byte[] getBytes() { 41 | return bytes; 42 | } 43 | 44 | public void setBytes(byte[] bytes) { 45 | this.bytes = bytes; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "ConstantUtf8Info{" + 51 | "tag=" + tag + 52 | ", length=" + length + 53 | ", bytes=" + Arrays.toString(bytes) + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/destiny/jvm/loader/ClassFileAnalyserTest.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.loader; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.destiny.jvm.model.ClassFile; 5 | import org.destiny.jvm.model.MethodInfo; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | /** 12 | * @author 王康 13 | * destinywk@163.com 14 | * ------------------------------------------------------------------ 15 | *

16 | * ------------------------------------------------------------------ 17 | * Corpright 2017 Destiny, Org. All rights reserved. 18 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 19 | * @version JDK 1.8.0_101 20 | * @since 2017/8/22 16:38 21 | */ 22 | public class ClassFileAnalyserTest { 23 | 24 | private ClassFileLoader classFileLoader; 25 | 26 | @Before 27 | public void before() { 28 | classFileLoader = new ClassFileLoader(); 29 | } 30 | 31 | @Test 32 | public void readBinaryCode() throws Exception { 33 | 34 | } 35 | 36 | @Test 37 | public void analysis() throws Exception { 38 | classFileLoader.addClassPath("/Users/destiny/IdeaProjects/coderising/coderising-02-jvm/src/main/java"); 39 | byte[] bytes = classFileLoader.readBinaryCode("org.destiny.jvm.test.Employee"); 40 | ClassFileAnalyser analyser = new ClassFileAnalyser(); 41 | ClassFile classFile = analyser.analysis(bytes); 42 | System.out.println(JSON.toJSONString(classFile)); 43 | 44 | MethodInfo method = classFile.getMethod("main", "([Ljava/lang/String;)V"); 45 | System.err.println(method); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/ConstantInfoEnum.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 常量池种类枚举 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public enum ConstantInfoEnum { 17 | 18 | CONSTANT_CLASS_INFO(7, "CONSTANT_CLASS_INFO"), 19 | CONSTANT_FIELD_INFO(9, "CONSTANT_FIELD_INFO"), 20 | CONSTANT_METHOD_INFO(10, "CONSTANT_METHOD_INFO"), 21 | CONSTANT_INTERFACE_INFO(11, "CONSTANT_INTERFACE_INFO"), 22 | CONSTANT_NAMEANDTYPE_INFO(12, "CONSTANT_NAMEANDTYPE_INFO"), 23 | CONSTANT_STRING_INFO(8, "CONSTANT_STRING_INFO"), 24 | CONSTANT_UTF8_INFO(1, "CONSTANT_UTF8_INFO"), 25 | ; 26 | 27 | 28 | private int tag; 29 | private String name; 30 | 31 | ConstantInfoEnum(int tag, String name) { 32 | this.tag = tag; 33 | this.name = name; 34 | } 35 | 36 | public int getTag() { 37 | return tag; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public static ConstantInfoEnum valueOf(int tag) { 45 | ConstantInfoEnum[] values = values(); 46 | for (ConstantInfoEnum constantInfoEnum : values) { 47 | if (constantInfoEnum.tag == tag) { 48 | return constantInfoEnum; 49 | } 50 | } 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/ConstantPool.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant; 2 | 3 | import org.destiny.jvm.model.constant.detail.AbstractConstantInfo; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * 常量池 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2017 Destiny, Org. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2017/8/22 16:38 19 | */ 20 | public class ConstantPool { 21 | 22 | private int length; 23 | private List constantInfoList; 24 | 25 | public ConstantPool() { 26 | } 27 | 28 | public ConstantPool(int length, List constantInfoList) { 29 | this.length = length; 30 | this.constantInfoList = constantInfoList; 31 | } 32 | 33 | public int getLength() { 34 | return length; 35 | } 36 | 37 | public void setLength(int length) { 38 | this.length = length; 39 | } 40 | 41 | public List getConstantInfoList() { 42 | return constantInfoList; 43 | } 44 | 45 | public void setConstantInfoList(List constantInfoList) { 46 | this.constantInfoList = constantInfoList; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "ConstantPool{" + 52 | "length=" + length + 53 | ", constantInfoList=" + constantInfoList + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.destiny.jvm 8 | minijvm 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | junit 14 | junit 15 | 4.12 16 | 17 | 18 | 19 | commons-io 20 | commons-io 21 | 2.5 22 | 23 | 24 | 25 | org.apache.commons 26 | commons-lang3 27 | 3.7 28 | 29 | 30 | 31 | com.alibaba 32 | fastjson 33 | 1.2.35 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 43 | 1.8 44 | 1.8 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/GetFieldCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class GetFieldCmd extends TwoOperandCmd { 19 | 20 | private String opCode; 21 | private int operand1; 22 | private int operand2; 23 | 24 | public GetFieldCmd(ConstantPool constantPool, String opCode, int operand1, int operand2) { 25 | super(constantPool); 26 | this.opCode = opCode; 27 | this.operand1 = operand1; 28 | this.operand2 = operand2; 29 | } 30 | 31 | @Override 32 | public void execute(StackFrame stackFrame, ExecutionResult result) { 33 | 34 | } 35 | 36 | public String getOpCode() { 37 | return opCode; 38 | } 39 | 40 | public int getOperand1() { 41 | return operand1; 42 | } 43 | 44 | public int getOperand2() { 45 | return operand2; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "GetFieldCmd{" + 51 | "opCode='" + opCode + '\'' + 52 | ", operand1=" + operand1 + 53 | ", operand2=" + operand2 + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/PutFieldCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class PutFieldCmd extends TwoOperandCmd { 19 | 20 | private String opCode; 21 | private int operand1; 22 | private int operand2; 23 | 24 | public PutFieldCmd(ConstantPool constantPool, String opCode, int operand1, int operand2) { 25 | super(constantPool); 26 | this.opCode = opCode; 27 | this.operand1 = operand1; 28 | this.operand2 = operand2; 29 | } 30 | 31 | @Override 32 | public void execute(StackFrame stackFrame, ExecutionResult result) { 33 | 34 | } 35 | 36 | public String getOpCode() { 37 | return opCode; 38 | } 39 | 40 | public int getOperand1() { 41 | return operand1; 42 | } 43 | 44 | public int getOperand2() { 45 | return operand2; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "PutFieldCmd{" + 51 | "opCode='" + opCode + '\'' + 52 | ", operand1=" + operand1 + 53 | ", operand2=" + operand2 + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/NewObjectCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class NewObjectCmd extends TwoOperandCmd { 19 | 20 | private String opCode; 21 | private int operand1; 22 | private int operand2; 23 | 24 | public NewObjectCmd(ConstantPool constantPool, String opCode, int operand1, int operand2) { 25 | super(constantPool); 26 | this.opCode = opCode; 27 | this.operand1 = operand1; 28 | this.operand2 = operand2; 29 | } 30 | 31 | @Override 32 | public void execute(StackFrame stackFrame, ExecutionResult result) { 33 | 34 | } 35 | 36 | public String getOpCode() { 37 | return opCode; 38 | } 39 | 40 | public int getOperand1() { 41 | return operand1; 42 | } 43 | 44 | public int getOperand2() { 45 | return operand2; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "NewObjectCmd{" + 51 | "opCode='" + opCode + '\'' + 52 | ", operand1=" + operand1 + 53 | ", operand2=" + operand2 + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/InvokeSpecialCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class InvokeSpecialCmd extends TwoOperandCmd { 19 | 20 | private String opCode; 21 | private int operand1; 22 | private int operand2; 23 | 24 | public InvokeSpecialCmd(ConstantPool constantPool, String opCode, int operand1, int operand2) { 25 | super(constantPool); 26 | this.opCode = opCode; 27 | this.operand1 = operand1; 28 | this.operand2 = operand2; 29 | } 30 | 31 | @Override 32 | public void execute(StackFrame stackFrame, ExecutionResult result) { 33 | 34 | } 35 | 36 | public String getOpCode() { 37 | return opCode; 38 | } 39 | 40 | public int getOperand1() { 41 | return operand1; 42 | } 43 | 44 | public int getOperand2() { 45 | return operand2; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "InvokeSpecialCmd{" + 51 | "opCode='" + opCode + '\'' + 52 | ", operand1=" + operand1 + 53 | ", operand2=" + operand2 + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/InvokeVirtualCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class InvokeVirtualCmd extends TwoOperandCmd { 19 | 20 | private String opCode; 21 | private int operand1; 22 | private int operand2; 23 | 24 | public InvokeVirtualCmd(ConstantPool constantPool, String opCode, int operand1, int operand2) { 25 | super(constantPool); 26 | this.opCode = opCode; 27 | this.operand1 = operand1; 28 | this.operand2 = operand2; 29 | } 30 | 31 | @Override 32 | public void execute(StackFrame stackFrame, ExecutionResult result) { 33 | 34 | } 35 | 36 | public String getOpCode() { 37 | return opCode; 38 | } 39 | 40 | public int getOperand1() { 41 | return operand1; 42 | } 43 | 44 | public int getOperand2() { 45 | return operand2; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "InvokeVirtualCmd{" + 51 | "opCode='" + opCode + '\'' + 52 | ", operand1=" + operand1 + 53 | ", operand2=" + operand2 + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/GetStaticFieldCmd.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.runtime.ExecutionResult; 5 | import org.destiny.jvm.model.runtime.StackFrame; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class GetStaticFieldCmd extends TwoOperandCmd { 19 | 20 | private String opCode; 21 | private int operand1; 22 | private int operand2; 23 | 24 | public GetStaticFieldCmd(ConstantPool constantPool, String opCode, int operand1, int operand2) { 25 | super(constantPool); 26 | this.opCode = opCode; 27 | this.operand1 = operand1; 28 | this.operand2 = operand2; 29 | } 30 | 31 | @Override 32 | public void execute(StackFrame stackFrame, ExecutionResult result) { 33 | 34 | } 35 | 36 | public String getOpCode() { 37 | return opCode; 38 | } 39 | 40 | public int getOperand1() { 41 | return operand1; 42 | } 43 | 44 | public int getOperand2() { 45 | return operand2; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "GetStaticFieldCmd{" + 51 | "opCode='" + opCode + '\'' + 52 | ", operand1=" + operand1 + 53 | ", operand2=" + operand2 + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/destiny/classplader/MyClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | package org.destiny.classplader; 2 | 3 | import org.junit.Test; 4 | 5 | import java.lang.reflect.Constructor; 6 | 7 | 8 | /** 9 | * @author 王康 10 | * destinywk@163.com 11 | * ------------------------------------------------------------------ 12 | *

13 | * ------------------------------------------------------------------ 14 | * Corpright 2017 Destiny, Org. All rights reserved. 15 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 16 | * @version JDK 1.8.0_101 17 | * @since 2017/8/22 16:38 18 | */ 19 | public class MyClassLoaderTest { 20 | 21 | @Test 22 | public void findClass() throws Exception { 23 | MyClassLoader myClassLoader = new MyClassLoader(); 24 | Class clazz = myClassLoader.findClass("org.destiny.jvm.test.Employee"); 25 | System.out.println(clazz.getClass()); 26 | // System.out.println(clazz instanceof org.destiny.jvm.test.Employee); 27 | Constructor[] constructors = clazz.getConstructors(); 28 | for (Constructor constructor : constructors) { 29 | System.out.println(constructor); 30 | // Object o = constructor.newInstance("destiny", 24); 31 | // System.out.println(o.toString()); 32 | } 33 | Constructor constructor = clazz.getConstructor(String.class, int.class); 34 | Object o = constructor.newInstance("destiny", 25); 35 | System.out.println(o); 36 | } 37 | 38 | @Test 39 | public void classPath() { 40 | System.out.println(this.getClass().getClassLoader().getResource("")); 41 | } 42 | 43 | public static void main(String[] args) { 44 | System.out.println("MyClassLoaderTest.main"); 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/ConstantFieldRefInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class ConstantFieldRefInfo extends AbstractConstantInfo { 15 | 16 | private int tag = 9; 17 | 18 | /** 19 | * 对常量池中 CONSTANTS_Class_info结构的有效索引 20 | */ 21 | private int classIndex; 22 | 23 | /** 24 | * 对常量池中 CONSTANTS_NameAndType_info的有效索引 25 | */ 26 | private int nameAndTypeIndex; 27 | 28 | public ConstantFieldRefInfo() { 29 | } 30 | 31 | public ConstantFieldRefInfo(int classIndex, int nameAndTypeIndex) { 32 | this.classIndex = classIndex; 33 | this.nameAndTypeIndex = nameAndTypeIndex; 34 | } 35 | 36 | public int getClassIndex() { 37 | return classIndex; 38 | } 39 | 40 | public void setClassIndex(int classIndex) { 41 | this.classIndex = classIndex; 42 | } 43 | 44 | public int getNameAndTypeIndex() { 45 | return nameAndTypeIndex; 46 | } 47 | 48 | public void setNameAndTypeIndex(int nameAndTypeIndex) { 49 | this.nameAndTypeIndex = nameAndTypeIndex; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "ConstantFieldRefInfo{" + 55 | "tag=" + tag + 56 | ", classIndex=" + classIndex + 57 | ", nameAndTypeIndex=" + nameAndTypeIndex + 58 | '}'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/ConstantMethodRefInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class ConstantMethodRefInfo extends AbstractConstantInfo { 15 | 16 | private int tag = 9; 17 | 18 | /** 19 | * 对常量池中 CONSTANTS_Class_info结构的有效索引 20 | */ 21 | private int classIndex; 22 | 23 | /** 24 | * 对常量池中 CONSTANTS_NameAndType_info的有效索引 25 | */ 26 | private int nameAndTypeIndex; 27 | 28 | public ConstantMethodRefInfo() { 29 | } 30 | 31 | public ConstantMethodRefInfo(int classIndex, int nameAndTypeIndex) { 32 | this.classIndex = classIndex; 33 | this.nameAndTypeIndex = nameAndTypeIndex; 34 | } 35 | 36 | public int getClassIndex() { 37 | return classIndex; 38 | } 39 | 40 | public void setClassIndex(int classIndex) { 41 | this.classIndex = classIndex; 42 | } 43 | 44 | public int getNameAndTypeIndex() { 45 | return nameAndTypeIndex; 46 | } 47 | 48 | public void setNameAndTypeIndex(int nameAndTypeIndex) { 49 | this.nameAndTypeIndex = nameAndTypeIndex; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "ConstantMethodRefInfo{" + 55 | "tag=" + tag + 56 | ", classIndex=" + classIndex + 57 | ", nameAndTypeIndex=" + nameAndTypeIndex + 58 | '}'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/constant/detail/ConstantNameAndTypeInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.constant.detail; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 用于表示字段或方法,但没有指明该字段或方法所属的类或接口 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class ConstantNameAndTypeInfo extends AbstractConstantInfo { 17 | 18 | private int tag = 12; 19 | 20 | /** 21 | * 对常量池中 CONSTANTS_Utf8_info结构的索引 22 | */ 23 | private int nameIndex; 24 | 25 | /** 26 | * 对常量池中 CONSTANTS_Utf8_info结构的索引 27 | */ 28 | private int descriptorIndex; 29 | 30 | public ConstantNameAndTypeInfo() { 31 | } 32 | 33 | public ConstantNameAndTypeInfo(int nameIndex, int descriptorIndex) { 34 | this.nameIndex = nameIndex; 35 | this.descriptorIndex = descriptorIndex; 36 | } 37 | 38 | public int getNameIndex() { 39 | return nameIndex; 40 | } 41 | 42 | public void setNameIndex(int nameIndex) { 43 | this.nameIndex = nameIndex; 44 | } 45 | 46 | public int getDescriptorIndex() { 47 | return descriptorIndex; 48 | } 49 | 50 | public void setDescriptorIndex(int descriptorIndex) { 51 | this.descriptorIndex = descriptorIndex; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "ConstantNameAndTypeInfo{" + 57 | "tag=" + tag + 58 | ", nameIndex=" + nameIndex + 59 | ", descriptorIndex=" + descriptorIndex + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/AccessFlagEnum.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 访问标志 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public enum AccessFlagEnum { 17 | 18 | PUBLIC("ACC_PUBLIC", 0x0001, "声明为public,可以从包外访问"), 19 | PRIVATE("ACC_PRIVATE", 0x0002, "声明为private"), 20 | PROTECTED("ACC_PROTECTED", 0x0004, "声明为protected"), 21 | STATIC("ACC_STATIC", 0x0008, "是否为static"), 22 | FINAL("ACC_FINAL", 0x0010, "是否为final"), 23 | SYNCHRONIZED("ACC_SYNCHRONIZED", 0x0020, "是否为synchronized"), 24 | SUPER("ACC_SUPER", 0x0020, "当用到invokespecial指令时,需要对父类方法做特殊处理"), 25 | INTERFACE("ACC_INTERFACE", 0x200, "该class文件定义的是接口而不是类"), 26 | ABSTRACT("ACC_ABSTRACT", 0x400, "声明为abstract,不能被实例化"), 27 | SYNTHETIC("ACC_SYNTHETIC", 0x1000, "表示该class文件并非由Java源代码所生成"), 28 | ANNOTATION("ACC_ANNOTATION", 0x2000, "标识注解类型"), 29 | ENUM("ACC_ENUM", 0x4000, "标识枚举类型") 30 | ; 31 | 32 | private String accessName; 33 | private int accessValue; 34 | private String accessDesc; 35 | 36 | AccessFlagEnum(String accessName, int accessValue, String accessDesc) { 37 | this.accessName = accessName; 38 | this.accessValue = accessValue; 39 | this.accessDesc = accessDesc; 40 | } 41 | 42 | public String getAccessName() { 43 | return accessName; 44 | } 45 | 46 | public int getAccessValue() { 47 | return accessValue; 48 | } 49 | 50 | public String getAccessDesc() { 51 | return accessDesc; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/hotswap/ByteUtils.java: -------------------------------------------------------------------------------- 1 | package org.destiny.hotswap; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * ------------------------------------------------------------------ 9 | * Corpright 2017 Destiny, Org. All rights reserved. 10 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 11 | * @version JDK 1.8.0_101 12 | * @since 2017/8/22 16:38 13 | */ 14 | public class ByteUtils { 15 | 16 | public static int bytes2Int(byte[] b, int start, int len) { 17 | int sum = 0; 18 | int end = start + len; 19 | for (int i = start; i < end; ++ i) { 20 | int n = b[i] & 0xff; 21 | n <<= (-- len) * 8; 22 | sum = n + sum; 23 | } 24 | return sum; 25 | } 26 | 27 | public static byte[] int2Bytes(int value, int len) { 28 | byte[] b = new byte[len]; 29 | for (int i = 0; i < len; ++ i) { 30 | b[len - i - 1] = (byte) ((value >> 8 * i) & 0xff); 31 | } 32 | return b; 33 | } 34 | 35 | public static String bytes2String(byte[] b, int start, int len) { 36 | return new String(b, start, len); 37 | } 38 | 39 | public static byte[] string2Bytes(String str) { 40 | return str.getBytes(); 41 | } 42 | 43 | public static byte[] bytesReplace(byte[] originalBytes, int offset, int len, byte[] replaceBytes) { 44 | byte[] newBytes = new byte[originalBytes.length + (replaceBytes.length - len)]; 45 | System.arraycopy(originalBytes, 0, newBytes, 0, offset); 46 | System.arraycopy(replaceBytes, 0, newBytes, offset, replaceBytes.length); 47 | System.arraycopy(originalBytes, offset + len, newBytes, offset + replaceBytes.length, originalBytes.length - offset - len); 48 | return newBytes; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/org/destiny/jvm/loader/ClassFileLoaderTest.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.loader; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | 8 | /** 9 | * @author 王康 10 | * destinywk@163.com 11 | * ------------------------------------------------------------------ 12 | *

13 | * ------------------------------------------------------------------ 14 | * Corpright 2017 Destiny, Org. All rights reserved. 15 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 16 | * @version JDK 1.8.0_101 17 | * @since 2017/8/22 16:38 18 | */ 19 | public class ClassFileLoaderTest { 20 | 21 | 22 | private ClassFileLoader classFileLoader; 23 | 24 | @Before 25 | public void before() { 26 | classFileLoader = new ClassFileLoader(); 27 | } 28 | 29 | @Test 30 | public void readBinaryCode() throws Exception { 31 | classFileLoader.addClassPath("/Users/destiny"); 32 | // System.out.println(classFileLoader.getClassPaths()); 33 | byte[] bytes = classFileLoader.readBinaryCode("Employee"); 34 | System.out.println(bytes.length); 35 | // parseToString(new byte[]{bytes[0], bytes[1], bytes[2], bytes[3]}); 36 | } 37 | 38 | @Test 39 | public void addClassPath() throws Exception { 40 | classFileLoader.addClassPath("/Users/destiny"); 41 | classFileLoader.addClassPath("/Users/guest"); 42 | // int[] ints = new int[]{1, 2}; 43 | System.out.println(classFileLoader.getClassPaths()); 44 | } 45 | 46 | @Test 47 | public void judgeMagic() { 48 | 49 | } 50 | 51 | private String parseToString(byte[] codes) throws UnsupportedEncodingException { 52 | StringBuilder stringBuilder = new StringBuilder(); 53 | for (byte b : codes) { 54 | System.out.println(b); 55 | } 56 | return null; 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/util/ConstantFactory.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.util; 2 | 3 | import org.destiny.jvm.model.constant.detail.*; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * 字节码文件的常量工厂,用于根据tag的不同返回不同常量 11 | * 目前暂时只实现 7,9,10,8,12,1 12 | *

13 | * ------------------------------------------------------------------ 14 | * Corpright 2017 Destiny, Org. All rights reserved. 15 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 16 | * @version JDK 1.8.0_101 17 | * @since 2017/8/22 16:38 18 | */ 19 | public class ConstantFactory { 20 | 21 | public AbstractConstantInfo create(ByteCodeIterator iterator) { 22 | int tag = iterator.readU1ToInt(); 23 | 24 | AbstractConstantInfo constantInfo = null; 25 | switch (tag) { 26 | case 1: 27 | int length = iterator.readU2ToInt(); 28 | constantInfo = new ConstantUtf8Info(length, iterator.read(length)); 29 | break; 30 | case 7: 31 | constantInfo = new ConstantClassInfo(iterator.readU2ToInt()); 32 | break; 33 | case 8: 34 | constantInfo = new ConstantStringInfo(iterator.readU2ToInt()); 35 | break; 36 | case 9: 37 | constantInfo = new ConstantFieldRefInfo(iterator.readU2ToInt(), iterator.readU2ToInt()); 38 | break; 39 | case 10: 40 | constantInfo = new ConstantMethodRefInfo(iterator.readU2ToInt(), iterator.readU2ToInt()); 41 | break; 42 | case 12: 43 | constantInfo = new ConstantNameAndTypeInfo(iterator.readU2ToInt(), iterator.readU2ToInt()); 44 | break; 45 | default: 46 | throw new IllegalArgumentException("所传参数无法对应常量池内容: " + tag); 47 | } 48 | return constantInfo; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/attr/ConstantValue.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.attr; 2 | 3 | import org.destiny.jvm.model.AbstractAttributeInfo; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * 定长属性 11 | * 如果该字段为静态字段,即field_info结构的access_flags项设置了ACC_STATIC标志 12 | * 则说明这个field_info结构所表示的字段,将赋值为它的ConstantValue属性所表示的值 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2017 Destiny, Org. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2017/8/22 16:38 19 | */ 20 | public class ConstantValue extends AbstractAttributeInfo { 21 | 22 | private int attributeNameIndex; // 常量池的索引 23 | private int attributeLength; // 固定为2 24 | private int constantValueIndex; // 常量池的索引,常量池在该位置给出了给出了该属性的常量值 25 | 26 | public int getAttributeNameIndex() { 27 | return attributeNameIndex; 28 | } 29 | 30 | public void setAttributeNameIndex(int attributeNameIndex) { 31 | this.attributeNameIndex = attributeNameIndex; 32 | } 33 | 34 | public int getAttributeLength() { 35 | return attributeLength; 36 | } 37 | 38 | public void setAttributeLength(int attributeLength) { 39 | this.attributeLength = attributeLength; 40 | } 41 | 42 | public int getConstantValueIndex() { 43 | return constantValueIndex; 44 | } 45 | 46 | public void setConstantValueIndex(int constantValueIndex) { 47 | this.constantValueIndex = constantValueIndex; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "ConstantValue{" + 53 | "attributeNameIndex=" + attributeNameIndex + 54 | ", attributeLength=" + attributeLength + 55 | ", constantValueIndex=" + constantValueIndex + 56 | '}'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/MethodArea.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm; 2 | 3 | import org.destiny.jvm.loader.ClassFileAnalyser; 4 | import org.destiny.jvm.loader.ClassFileLoader; 5 | import org.destiny.jvm.model.ClassFile; 6 | import org.destiny.jvm.model.MethodInfo; 7 | 8 | import java.io.FileNotFoundException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author 王康 14 | * hzwangkang1@corp.netease.com 15 | * ------------------------------------------------------------------ 16 | *

17 | * 方法区 18 | *

19 | * ------------------------------------------------------------------ 20 | * Corpright 2018 Netease, Inc. All rights reserved. 21 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 22 | * @version JDK 1.8.0_101 23 | * @since 2017/8/22 16:38 24 | */ 25 | public class MethodArea { 26 | 27 | private Map map = new HashMap<>(); 28 | 29 | private ClassFileLoader classFileLoader = new ClassFileLoader(); 30 | 31 | private ClassFileAnalyser classFileAnalyser = new ClassFileAnalyser(); 32 | 33 | private static MethodArea METHOD_AREA = new MethodArea(); 34 | 35 | private MethodArea() { 36 | } 37 | 38 | public static MethodArea getInstance() { 39 | return METHOD_AREA; 40 | } 41 | 42 | public MethodInfo getMainMethod(String className) throws FileNotFoundException, IllegalAccessException { 43 | ClassFile classFile = findClassFile(className); 44 | return classFile.getMainMethod(); 45 | } 46 | 47 | /** 48 | * 返回类的加载对象, 如果之前没有被加载, 则加载后再返回 49 | * 50 | * @param className 51 | * @return 52 | * @throws FileNotFoundException 53 | * @throws IllegalAccessException 54 | */ 55 | public ClassFile findClassFile(String className) throws FileNotFoundException, IllegalAccessException { 56 | // 如果已经被加载过, 直接返回 57 | if (map.get(className) != null) { 58 | return map.get(className); 59 | } 60 | 61 | ClassFile classFile = classFileAnalyser.analysis(classFileLoader.readBinaryCode(className)); 62 | map.put(className, classFile); 63 | return classFile; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/FieldInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class FieldInfo { 17 | 18 | private int accessFlags; 19 | private int nameIndex; 20 | private int descriptorIndex; 21 | private int attributesCount; 22 | private List attributes; 23 | 24 | public int getAccessFlags() { 25 | return accessFlags; 26 | } 27 | 28 | public void setAccessFlags(int accessFlags) { 29 | this.accessFlags = accessFlags; 30 | } 31 | 32 | public int getNameIndex() { 33 | return nameIndex; 34 | } 35 | 36 | public void setNameIndex(int nameIndex) { 37 | this.nameIndex = nameIndex; 38 | } 39 | 40 | public int getDescriptorIndex() { 41 | return descriptorIndex; 42 | } 43 | 44 | public void setDescriptorIndex(int descriptorIndex) { 45 | this.descriptorIndex = descriptorIndex; 46 | } 47 | 48 | public int getAttributesCount() { 49 | return attributesCount; 50 | } 51 | 52 | public void setAttributesCount(int attributesCount) { 53 | this.attributesCount = attributesCount; 54 | } 55 | 56 | public List getAttributes() { 57 | return attributes; 58 | } 59 | 60 | public void setAttributes(List attributes) { 61 | this.attributes = attributes; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "FieldInfo{" + 67 | "accessFlags=" + accessFlags + 68 | ", nameIndex=" + nameIndex + 69 | ", descriptorIndex=" + descriptorIndex + 70 | ", attributesCount=" + attributesCount + 71 | ", attributes=" + attributes + 72 | '}'; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/MethodInfo.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author 王康 7 | * destinywk@163.com 8 | * ------------------------------------------------------------------ 9 | *

方法

10 | * ------------------------------------------------------------------ 11 | * Corpright 2017 Destiny, Org. All rights reserved. 12 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 13 | * @version JDK 1.8.0_101 14 | * @since 2017/8/22 16:38 15 | */ 16 | public class MethodInfo { 17 | 18 | private int accessFlags; 19 | private int nameIndex; 20 | private int descriptorIndex; 21 | private int attributesCount; 22 | private List attributes; 23 | 24 | public int getAccessFlags() { 25 | return accessFlags; 26 | } 27 | 28 | public void setAccessFlags(int accessFlags) { 29 | this.accessFlags = accessFlags; 30 | } 31 | 32 | public int getNameIndex() { 33 | return nameIndex; 34 | } 35 | 36 | public void setNameIndex(int nameIndex) { 37 | this.nameIndex = nameIndex; 38 | } 39 | 40 | public int getDescriptorIndex() { 41 | return descriptorIndex; 42 | } 43 | 44 | public void setDescriptorIndex(int descriptorIndex) { 45 | this.descriptorIndex = descriptorIndex; 46 | } 47 | 48 | public int getAttributesCount() { 49 | return attributesCount; 50 | } 51 | 52 | public void setAttributesCount(int attributesCount) { 53 | this.attributesCount = attributesCount; 54 | } 55 | 56 | public List getAttributes() { 57 | return attributes; 58 | } 59 | 60 | public void setAttributes(List attributes) { 61 | this.attributes = attributes; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "MethodInfo{" + 67 | "accessFlags=" + accessFlags + 68 | ", nameIndex=" + nameIndex + 69 | ", descriptorIndex=" + descriptorIndex + 70 | ", attributesCount=" + attributesCount + 71 | ", attributes=" + attributes + 72 | '}'; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/AccessFlag.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author 王康 8 | * destinywk@163.com 9 | * ------------------------------------------------------------------ 10 | *

11 | * ------------------------------------------------------------------ 12 | * Corpright 2017 Destiny, Org. All rights reserved. 13 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 14 | * @version JDK 1.8.0_101 15 | * @since 2017/8/22 16:38 16 | */ 17 | public class AccessFlag { 18 | 19 | private static final int ACC_PUBLIC = 0x0001; 20 | private static final int ACC_PRIVATE = 0x0002; 21 | private static final int ACC_FINAL = 0x0010; 22 | private static final int ACC_SUPER = 0x0020; 23 | private static final int ACC_INTERFACE = 0x0200; 24 | private static final int ACC_ABSTRACT = 0x0400; 25 | private static final int ACC_SYNTHETIC = 0x1000; 26 | private static final int ANNOTATION = 0x2000; 27 | private static final int ENUM = 0x4000; 28 | 29 | public static List getAccessFlag(int flag) { 30 | List flags = new ArrayList<>(8); 31 | if ((flag & AccessFlagEnum.PUBLIC.getAccessValue()) != 0) { 32 | flags.add(AccessFlagEnum.PUBLIC.getAccessName()); 33 | } 34 | if ((flag & AccessFlagEnum.FINAL.getAccessValue()) != 0) { 35 | flags.add(AccessFlagEnum.FINAL.getAccessName()); 36 | } 37 | if ((flag & AccessFlagEnum.SUPER.getAccessValue()) != 0) { 38 | flags.add(AccessFlagEnum.SUPER.getAccessName()); 39 | } 40 | if ((flag & AccessFlagEnum.INTERFACE.getAccessValue()) != 0) { 41 | flags.add(AccessFlagEnum.INTERFACE.getAccessName()); 42 | } 43 | if ((flag & AccessFlagEnum.ABSTRACT.getAccessValue()) != 0) { 44 | flags.add(AccessFlagEnum.ABSTRACT.getAccessName()); 45 | } 46 | if ((flag & AccessFlagEnum.SYNTHETIC.getAccessValue()) != 0) { 47 | flags.add(AccessFlagEnum.SYNTHETIC.getAccessName()); 48 | } 49 | if ((flag & AccessFlagEnum.ANNOTATION.getAccessValue()) != 0) { 50 | flags.add(AccessFlagEnum.ANNOTATION.getAccessName()); 51 | } 52 | if ((flag & AccessFlagEnum.ENUM.getAccessValue()) != 0) { 53 | flags.add(AccessFlagEnum.ENUM.getAccessName()); 54 | } 55 | return flags; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/util/ByteCodeIterator.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.util; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 字节数组迭代器,自身维护一个字节数组的下标 9 | * 用于按照指定要求遍历字节数组,并返回不同格式数据 10 | *

11 | * ------------------------------------------------------------------ 12 | * Corpright 2017 Destiny, Org. All rights reserved. 13 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 14 | * @version JDK 1.8.0_101 15 | * @since 2017/8/22 16:38 16 | */ 17 | public class ByteCodeIterator { 18 | 19 | private final byte[] codes; 20 | 21 | private int index; 22 | 23 | public ByteCodeIterator(byte[] codes) { 24 | this.codes = codes; 25 | index = 0; 26 | } 27 | 28 | public byte[] read(int length) { 29 | byte[] bytes = new byte[length]; 30 | for (int i = 0; i < length; ++ i) { 31 | bytes[i] = codes[index++]; 32 | } 33 | return bytes; 34 | } 35 | 36 | public String readU4ToString() { 37 | return parse2String(new byte[]{codes[index++], codes[index++], codes[index++], codes[index++]}); 38 | } 39 | 40 | public int readU1ToInt() { 41 | return Byte.toUnsignedInt(codes[index++]); 42 | } 43 | 44 | public int readU2ToInt() { 45 | return parse2Int(new byte[]{codes[index++], codes[index++]}); 46 | } 47 | 48 | public int readU4ToInt() { 49 | return parse2Int(new byte[]{codes[index++], codes[index++], codes[index++], codes[index++]}); 50 | } 51 | 52 | /** 53 | * 将字节数组转换成字符串 54 | * @param codes 字节流 55 | * @return 56 | */ 57 | public String parse2String(byte[] codes) { 58 | StringBuilder stringBuilder = new StringBuilder(); 59 | for (int i = 0; i < codes.length; ++ i) { 60 | byte b = codes[i]; 61 | int value = b & 0xFF; 62 | String strHex = Integer.toHexString(value); 63 | if (strHex.length() < 2) { 64 | strHex = "0" + strHex; 65 | } 66 | stringBuilder.append(strHex); 67 | } 68 | return stringBuilder.toString(); 69 | } 70 | 71 | 72 | private int parse2Int(byte[] bytes) { 73 | int result = 0; 74 | for (int i = 0; i < bytes.length; ++ i) { 75 | result = result | (bytes[bytes.length - 1 - i] & 0xFF) << (i << 3); 76 | } 77 | return result; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/classplader/MyClassLoader.java: -------------------------------------------------------------------------------- 1 | package org.destiny.classplader; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | 5 | import java.io.*; 6 | 7 | /** 8 | * @author 王康 9 | * destinywk@163.com 10 | * ------------------------------------------------------------------ 11 | *

12 | * ------------------------------------------------------------------ 13 | * Corpright 2017 Destiny, Org. All rights reserved. 14 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 15 | * @version JDK 1.8.0_101 16 | * @since 2017/8/22 16:38 17 | */ 18 | public class MyClassLoader extends ClassLoader { 19 | 20 | @Override 21 | protected Class findClass(String name) throws ClassNotFoundException { 22 | // 获取字节数组 23 | byte[] bytes = loadByteCode(name); 24 | if (bytes == null) { 25 | throw new ClassNotFoundException(); 26 | } else { 27 | // 28 | return defineClass(name, bytes, 0, bytes.length); 29 | } 30 | } 31 | 32 | private byte[] loadByteCode(String name) { 33 | String fileName = "/Users/destiny" 34 | + File.separatorChar + name.replace('.', File.separatorChar) + ".class"; 35 | try (InputStream inputStream = new FileInputStream(fileName); 36 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { 37 | byte[] buffer = new byte[1024]; 38 | int length = 0; 39 | while ((length = inputStream.read(buffer)) != -1) { 40 | byteArrayOutputStream.write(buffer, 0, length); 41 | } 42 | return byteArrayOutputStream.toByteArray(); 43 | } catch (FileNotFoundException e) { 44 | e.printStackTrace(); 45 | } catch (IOException e) { 46 | e.printStackTrace(); 47 | } 48 | return null; 49 | } 50 | 51 | private byte[] loadClassFile(String classFileName) { 52 | File file = new File(classFileName); 53 | try { 54 | return IOUtils.toByteArray(new FileInputStream(file)); 55 | } catch (FileNotFoundException e) { 56 | e.printStackTrace(); 57 | } catch (IOException e) { 58 | e.printStackTrace(); 59 | } 60 | return null; 61 | } 62 | 63 | public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { 64 | MyClassLoader myClassLoader = new MyClassLoader(); 65 | Class clazz = Class.forName("org.destiny.classplader.MyClassLoader", true, myClassLoader); 66 | Object o = clazz.newInstance(); 67 | System.out.println(o); 68 | System.out.println(o.getClass().getClassLoader()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/hotswap/ClassModifier.java: -------------------------------------------------------------------------------- 1 | package org.destiny.hotswap; 2 | 3 | /** 4 | * @author 王康 5 | * destinywk@163.com 6 | * ------------------------------------------------------------------ 7 | *

8 | * 修改class文件 9 | * 目前只提供修改常量池常量的功能 10 | *

11 | * ------------------------------------------------------------------ 12 | * Corpright 2017 Destiny, Org. All rights reserved. 13 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 14 | * @version JDK 1.8.0_101 15 | * @since 2017/8/22 16:38 16 | */ 17 | public class ClassModifier { 18 | 19 | /** 20 | * Class文件中常量池的起始偏移量 21 | */ 22 | private static final int CONSTANT_POOL_COUNT_INDEX = 8; 23 | 24 | /** 25 | * CONSTANT_Utf8_info常量的tag标志 26 | */ 27 | private static final int CONSTANT_Utf8_info = 1; 28 | 29 | /** 30 | * 常量池中11中常量所占的长度 31 | */ 32 | private static final int[] CONSTANT_ITEM_LENGTH = {-1, -1, -1, 5, 5, 9, 9, 3, 3, 5, 5, 5, 5}; 33 | 34 | private static final int u1 = 1; 35 | private static final int u2 = 2; 36 | 37 | private byte[] classByte; 38 | 39 | public ClassModifier(byte[] classByte) { 40 | this.classByte = classByte; 41 | } 42 | 43 | /** 44 | * 修改常量池中CONSTANT_Utf8_info常量的内容 45 | * @param oldStr 46 | * @param newStr 47 | * @return 48 | */ 49 | public byte[] modifyUTF8Constant(String oldStr, String newStr) { 50 | int cpc = getConstantPoolCount(); 51 | int offset = CONSTANT_POOL_COUNT_INDEX + u2; 52 | for (int i = 0; i < cpc; ++ i) { 53 | int tag = ByteUtils.bytes2Int(classByte, offset, u1); 54 | if (tag == CONSTANT_Utf8_info) { 55 | int len = ByteUtils.bytes2Int(classByte, offset + u1, u2); 56 | offset += (u1 + u2); 57 | String str = ByteUtils.bytes2String(classByte, offset, len); 58 | if (str.equalsIgnoreCase(oldStr)) { 59 | byte[] strBytes = ByteUtils.string2Bytes(newStr); 60 | byte[] strlen = ByteUtils.int2Bytes(newStr.length(), u2); 61 | classByte = ByteUtils.bytesReplace(classByte, offset - u2, u2, strlen); 62 | classByte = ByteUtils.bytesReplace(classByte, offset, len, strBytes); 63 | } else { 64 | offset += len; 65 | } 66 | } else { 67 | offset += CONSTANT_ITEM_LENGTH[tag]; 68 | } 69 | } 70 | return classByte; 71 | } 72 | 73 | /** 74 | * 获取常量池中常量的数量 75 | * @return 76 | */ 77 | public int getConstantPoolCount() { 78 | return ByteUtils.bytes2Int(classByte, CONSTANT_POOL_COUNT_INDEX, u2); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/attr/LineNumberTable.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.attr; 2 | 3 | import org.destiny.jvm.util.ByteCodeIterator; 4 | import org.destiny.jvm.model.AbstractAttributeInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author 王康 10 | * destinywk@163.com 11 | * ------------------------------------------------------------------ 12 | *

13 | * ------------------------------------------------------------------ 14 | * Corpright 2017 Destiny, Org. All rights reserved. 15 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 16 | * @version JDK 1.8.0_101 17 | * @since 2017/8/22 16:38 18 | */ 19 | public class LineNumberTable extends AbstractAttributeInfo { 20 | 21 | private int attributeNameIndex; // 指向常量池,一定是LineNumberTable 22 | private int attributeLength; // 当前属性长度,不包括开始的6个字节 23 | private int lineNumberTableLength; // 成员数量 24 | private List lineNumberTable; 25 | 26 | public LineNumber generateLineNumber(ByteCodeIterator iterator) { 27 | LineNumber lineNumber = new LineNumber(); 28 | lineNumber.setStartPc(iterator.readU2ToInt()); 29 | lineNumber.setLineNumber(iterator.readU2ToInt()); 30 | return lineNumber; 31 | } 32 | 33 | public int getAttributeNameIndex() { 34 | return attributeNameIndex; 35 | } 36 | 37 | public void setAttributeNameIndex(int attributeNameIndex) { 38 | this.attributeNameIndex = attributeNameIndex; 39 | } 40 | 41 | public int getAttributeLength() { 42 | return attributeLength; 43 | } 44 | 45 | public void setAttributeLength(int attributeLength) { 46 | this.attributeLength = attributeLength; 47 | } 48 | 49 | public int getLineNumberTableLength() { 50 | return lineNumberTableLength; 51 | } 52 | 53 | public void setLineNumberTableLength(int lineNumberTableLength) { 54 | this.lineNumberTableLength = lineNumberTableLength; 55 | } 56 | 57 | public List getLineNumberTable() { 58 | return lineNumberTable; 59 | } 60 | 61 | public void setLineNumberTable(List lineNumberTable) { 62 | this.lineNumberTable = lineNumberTable; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "LineNumberTable{" + 68 | "attributeNameIndex=" + attributeNameIndex + 69 | ", attributeLength=" + attributeLength + 70 | ", lineNumberTableLength=" + lineNumberTableLength + 71 | ", lineNumberTable=" + lineNumberTable + 72 | '}'; 73 | } 74 | 75 | public static class LineNumber { 76 | private int startPc; // code[]数组的一个索引 77 | private int lineNumber; // 源文件的行号 78 | 79 | public int getStartPc() { 80 | return startPc; 81 | } 82 | 83 | public void setStartPc(int startPc) { 84 | this.startPc = startPc; 85 | } 86 | 87 | public int getLineNumber() { 88 | return lineNumber; 89 | } 90 | 91 | public void setLineNumber(int lineNumber) { 92 | this.lineNumber = lineNumber; 93 | } 94 | 95 | @Override 96 | public String toString() { 97 | return "LineNumber{" + 98 | "startPc=" + startPc + 99 | ", lineNumber=" + lineNumber + 100 | '}'; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/loader/ClassFileLoader.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.loader; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | 5 | import java.io.*; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * @author 王康 11 | * destinywk@163.com 12 | * ------------------------------------------------------------------ 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2017 Destiny, Org. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2017/8/22 16:38 19 | */ 20 | public class ClassFileLoader { 21 | 22 | /** 23 | * 用于存储loader的所有classpath 24 | */ 25 | private List classPaths = new LinkedList<>(); 26 | 27 | /** 28 | * 添加一个classpath 29 | * @param path 30 | */ 31 | public void addClassPath(String path) { 32 | classPaths.add(path); 33 | } 34 | 35 | /** 36 | * 魔数 37 | */ 38 | private static final String MAGIC_NUMBER = "cafebabe"; 39 | 40 | /** 41 | * 返回classPath 42 | * @return 43 | */ 44 | public String getClassPaths() { 45 | StringBuilder stringBuilder = new StringBuilder(); 46 | for (String path : classPaths) { 47 | stringBuilder.append(path).append(";"); 48 | } 49 | return stringBuilder.substring(0, stringBuilder.length() - 1); 50 | } 51 | 52 | /** 53 | * 将指定classpath和class文件的相对路径进行拼接,返回文件的字节流 54 | * @param className class文件的相对路径 55 | * @return 56 | * @throws IllegalAccessException 57 | * @throws FileNotFoundException 58 | */ 59 | public byte[] readBinaryCode(String className) throws IllegalAccessException, FileNotFoundException { 60 | if (classPaths.isEmpty()) { 61 | throw new IllegalAccessException("classPaths未设置"); 62 | } 63 | className = className.replace('.', File.separatorChar) + ".class"; 64 | for (String path : classPaths) { 65 | String classFileName = path + File.separatorChar + className; 66 | byte[] codes = loadClassFile(classFileName); 67 | if (codes != null) { 68 | return codes; 69 | } 70 | } 71 | return null; 72 | } 73 | 74 | private byte[] loadClassFile(String classFileName) { 75 | File file = new File(classFileName); 76 | try { 77 | return IOUtils.toByteArray(new FileInputStream(file)); 78 | } catch (FileNotFoundException e) { 79 | e.printStackTrace(); 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | } 83 | return null; 84 | } 85 | 86 | private byte[] loadClassFileOld(String classFileName) { 87 | File file = new File(classFileName); 88 | if (file.exists()) { 89 | try (FileInputStream fileInputStream = new FileInputStream(file); 90 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { 91 | byte[] buffer = new byte[1024]; 92 | int len; 93 | while ((len = fileInputStream.read(buffer)) > 0) { 94 | byteArrayOutputStream.write(buffer, 0, len); 95 | } 96 | return byteArrayOutputStream.toByteArray(); 97 | } catch (IOException e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | return null; 102 | } 103 | 104 | /** 105 | * 验证该字节流的前8个字节是否是class文件的魔数 106 | * @param codes 107 | * @return 108 | */ 109 | private boolean checkMagicNumber(byte[] codes) { 110 | return MAGIC_NUMBER.equals(byteToHexString(codes)); 111 | } 112 | 113 | /** 114 | * 将字节数组转换成字符串 115 | * @param codes 字节流 116 | * @return 117 | */ 118 | private String byteToHexString(byte[] codes) { 119 | StringBuilder stringBuilder = new StringBuilder(); 120 | for (int i = 0; i < codes.length; ++ i) { 121 | byte b = codes[i]; 122 | int value = b & 0xFF; 123 | String strHex = Integer.toHexString(value); 124 | if (strHex.length() < 2) { 125 | strHex = "0" + strHex; 126 | } 127 | stringBuilder.append(strHex); 128 | } 129 | return stringBuilder.toString(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/attr/LocalVariableTable.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.attr; 2 | 3 | import org.destiny.jvm.util.ByteCodeIterator; 4 | import org.destiny.jvm.model.AbstractAttributeInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author 王康 10 | * destinywk@163.com 11 | * ------------------------------------------------------------------ 12 | *

13 | * ------------------------------------------------------------------ 14 | * Corpright 2017 Destiny, Org. All rights reserved. 15 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 16 | * @version JDK 1.8.0_101 17 | * @since 2017/8/22 16:38 18 | */ 19 | public class LocalVariableTable extends AbstractAttributeInfo { 20 | 21 | private int attributeNameIndex; // 指向常量池,LocalVariableTable 22 | private int attributeLength; // 当前属性长度,不包括开始的6个字节 23 | private int localVariableTableLength; // 给出了下面localVariableTable[]数组成员的数量 24 | private List localVariableTable; 25 | 26 | public LocalVariable generateLocalVariable(ByteCodeIterator iterator) { 27 | LocalVariable localVariable = new LocalVariable(); 28 | localVariable.setStartPc(iterator.readU2ToInt()); 29 | localVariable.setLength(iterator.readU2ToInt()); 30 | localVariable.setNameIndex(iterator.readU2ToInt()); 31 | localVariable.setDescriptorIndex(iterator.readU2ToInt()); 32 | localVariable.setIndex(iterator.readU1ToInt()); 33 | return localVariable; 34 | } 35 | 36 | public int getAttributeNameIndex() { 37 | return attributeNameIndex; 38 | } 39 | 40 | public void setAttributeNameIndex(int attributeNameIndex) { 41 | this.attributeNameIndex = attributeNameIndex; 42 | } 43 | 44 | public int getAttributeLength() { 45 | return attributeLength; 46 | } 47 | 48 | public void setAttributeLength(int attributeLength) { 49 | this.attributeLength = attributeLength; 50 | } 51 | 52 | public int getLocalVariableTableLength() { 53 | return localVariableTableLength; 54 | } 55 | 56 | public void setLocalVariableTableLength(int localVariableTableLength) { 57 | this.localVariableTableLength = localVariableTableLength; 58 | } 59 | 60 | public List getLocalVariableTable() { 61 | return localVariableTable; 62 | } 63 | 64 | public void setLocalVariableTable(List localVariableTable) { 65 | this.localVariableTable = localVariableTable; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "LocalVariableTable{" + 71 | "attributeNameIndex=" + attributeNameIndex + 72 | ", attributeLength=" + attributeLength + 73 | ", localVariableTableLength=" + localVariableTableLength + 74 | ", localVariableTable=" + localVariableTable + 75 | '}'; 76 | } 77 | 78 | public static class LocalVariable { 79 | private int startPc; // 局部变量表的索引都在范围[startPc, 80 | private int length; // startPc + length)中 81 | private int nameIndex; // 变量名索引(在常量池中) 82 | private int descriptorIndex; // 变量描述索引(在常量池中) 83 | private int index; // 此局部变量在当前栈帧的局部变量表中的索引 84 | 85 | public int getStartPc() { 86 | return startPc; 87 | } 88 | 89 | public void setStartPc(int startPc) { 90 | this.startPc = startPc; 91 | } 92 | 93 | public int getLength() { 94 | return length; 95 | } 96 | 97 | public void setLength(int length) { 98 | this.length = length; 99 | } 100 | 101 | public int getNameIndex() { 102 | return nameIndex; 103 | } 104 | 105 | public void setNameIndex(int nameIndex) { 106 | this.nameIndex = nameIndex; 107 | } 108 | 109 | public int getDescriptorIndex() { 110 | return descriptorIndex; 111 | } 112 | 113 | public void setDescriptorIndex(int descriptorIndex) { 114 | this.descriptorIndex = descriptorIndex; 115 | } 116 | 117 | public int getIndex() { 118 | return index; 119 | } 120 | 121 | public void setIndex(int index) { 122 | this.index = index; 123 | } 124 | 125 | @Override 126 | public String toString() { 127 | return "LocalVariable{" + 128 | "startPc=" + startPc + 129 | ", length=" + length + 130 | ", nameIndex=" + nameIndex + 131 | ", descriptorIndex=" + descriptorIndex + 132 | ", index=" + index + 133 | '}'; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/loader/ClassFileAnalyser.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.loader; 2 | 3 | import org.destiny.jvm.util.AttributeFactory; 4 | import org.destiny.jvm.util.ByteCodeIterator; 5 | import org.destiny.jvm.util.ConstantFactory; 6 | import org.destiny.jvm.model.*; 7 | import org.destiny.jvm.model.constant.ConstantPool; 8 | import org.destiny.jvm.model.constant.detail.AbstractConstantInfo; 9 | 10 | import java.util.ArrayList; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | 14 | /** 15 | * @author 王康 16 | * destinywk@163.com 17 | * ------------------------------------------------------------------ 18 | *

19 | * 类的解析器,负责根据读入的字节流解析class文件 20 | *

21 | * ------------------------------------------------------------------ 22 | * Corpright 2017 Destiny, Org. All rights reserved. 23 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 24 | * @version JDK 1.8.0_101 25 | * @since 2017/8/22 16:38 26 | */ 27 | public class ClassFileAnalyser { 28 | 29 | private ConstantFactory constantFactory; 30 | 31 | private AttributeFactory attributeFactory; 32 | 33 | private ClassFile classFile; 34 | 35 | public ClassFile analysis(byte[] codes) { 36 | classFile = new ClassFile(); 37 | // 字节码迭代器 38 | ByteCodeIterator iterator = new ByteCodeIterator(codes); 39 | classFile.setMagicNumber(iterator.readU4ToString()); 40 | classFile.setMinorVersion(iterator.readU2ToInt()); 41 | classFile.setMajorVersion(iterator.readU2ToInt()); 42 | int length = iterator.readU2ToInt(); 43 | ConstantPool constantPool = new ConstantPool(length, generateConstantPool(iterator, length)); 44 | classFile.setConstantPool(constantPool); 45 | classFile.setAccessFlags(accessFlag(iterator)); 46 | classFile.setThisClass(iterator.readU2ToInt()); 47 | classFile.setSuperClass(iterator.readU2ToInt()); 48 | int interfacesCount = iterator.readU2ToInt(); 49 | classFile.setInterfacesCount(interfacesCount); 50 | List interfaces = new LinkedList<>(); 51 | for (int i = 0; i < interfacesCount; ++ i) { 52 | interfaces.add(iterator.readU2ToInt()); 53 | } 54 | classFile.setInterfaces(interfaces); 55 | int fieldCount = iterator.readU2ToInt(); 56 | classFile.setFieldsCount(fieldCount); 57 | classFile.setFields(generateFields(iterator, fieldCount)); 58 | int methodCount = iterator.readU2ToInt(); 59 | classFile.setMethodCount(methodCount); 60 | classFile.setMethodInfos(generateMethods(iterator, methodCount, constantPool)); 61 | return classFile; 62 | } 63 | 64 | private List generateConstantPool(ByteCodeIterator iterator, int length) { 65 | constantFactory = new ConstantFactory(); 66 | List constantInfoList = new ArrayList<>(length + 1); 67 | constantInfoList.add(new AbstractConstantInfo() {}); 68 | for (int i = 1; i < length; ++ i) { 69 | constantInfoList.add(constantFactory.create(iterator)); 70 | } 71 | return constantInfoList; 72 | } 73 | 74 | private List accessFlag(ByteCodeIterator iterator) { 75 | int accessFlag = iterator.readU2ToInt(); 76 | return AccessFlag.getAccessFlag(accessFlag); 77 | } 78 | 79 | private List generateFields(ByteCodeIterator iterator, int count) { 80 | List fieldInfoList = new LinkedList<>(); 81 | for (int i = 0; i < count; ++ i) { 82 | FieldInfo fieldInfo = new FieldInfo(); 83 | fieldInfo.setAccessFlags(iterator.readU2ToInt()); 84 | fieldInfo.setNameIndex(iterator.readU2ToInt()); 85 | fieldInfo.setDescriptorIndex(iterator.readU2ToInt()); 86 | int attrCount = iterator.readU2ToInt(); 87 | for (int j = 0; j < attrCount; ++ j) { 88 | // TODO 字段的属性解析,目前还没有实现 89 | } 90 | fieldInfoList.add(fieldInfo); 91 | } 92 | return fieldInfoList; 93 | } 94 | 95 | private List generateMethods(ByteCodeIterator iterator, int count, ConstantPool constantPool) { 96 | attributeFactory = new AttributeFactory(); 97 | List methodInfoList = new LinkedList<>(); 98 | for (int i = 0; i < count; ++ i) { 99 | MethodInfo methodInfo = new MethodInfo(); 100 | methodInfo.setAccessFlags(iterator.readU2ToInt()); 101 | methodInfo.setNameIndex(iterator.readU2ToInt()); 102 | methodInfo.setDescriptorIndex(iterator.readU2ToInt()); 103 | int attrCount = iterator.readU2ToInt(); 104 | methodInfo.setAttributesCount(attrCount); 105 | List abstractAttributeInfoList = new LinkedList<>(); 106 | for (int j = 0; j < attrCount; ++ j) { 107 | abstractAttributeInfoList.add(attributeFactory.create(iterator, constantPool)); 108 | } 109 | methodInfo.setAttributes(abstractAttributeInfoList); 110 | methodInfoList.add(methodInfo); 111 | } 112 | return methodInfoList; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/util/AttributeFactory.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.util; 2 | 3 | import org.destiny.jvm.model.AbstractAttributeInfo; 4 | import org.destiny.jvm.model.attr.Code; 5 | import org.destiny.jvm.model.attr.LineNumberTable; 6 | import org.destiny.jvm.model.attr.LocalVariableTable; 7 | import org.destiny.jvm.model.command.ByteCodeCmd; 8 | import org.destiny.jvm.model.command.CommandParser; 9 | import org.destiny.jvm.model.constant.ConstantPool; 10 | import org.destiny.jvm.model.constant.detail.ConstantUtf8Info; 11 | 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | 15 | /** 16 | * @author 王康 17 | * destinywk@163.com 18 | * ------------------------------------------------------------------ 19 | *

20 | * 属性工厂,用于生产和返回属性 21 | *

22 | * ------------------------------------------------------------------ 23 | * Corpright 2017 Destiny, Org. All rights reserved. 24 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 25 | * @version JDK 1.8.0_101 26 | * @since 2017/8/22 16:38 27 | */ 28 | public class AttributeFactory { 29 | 30 | private static final String CODE = "Code"; 31 | private static final String LINE_NUMBER_TABLE = "LineNumberTable"; 32 | private static final String CONSTANT_VALUE = "ConstantValue"; 33 | private static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable"; 34 | private static final String STACK_MAP_TABLE = "StackMapTable"; 35 | 36 | public AbstractAttributeInfo create(ByteCodeIterator iterator, ConstantPool constantPool) { 37 | int nameIndex = iterator.readU2ToInt(); 38 | AbstractAttributeInfo abstractAttributeInfo = null; 39 | String content = new String(((ConstantUtf8Info) constantPool.getConstantInfoList().get(nameIndex)).getBytes()); 40 | switch (content) { 41 | case CODE: 42 | Code code = new Code(); 43 | code.setAttributeNameIndex(nameIndex); 44 | code.setAttributeLength(iterator.readU4ToInt()); 45 | code.setMaxStack(iterator.readU2ToInt()); 46 | code.setMaxLocals(iterator.readU2ToInt()); 47 | int codeLength = iterator.readU4ToInt(); 48 | code.setCodeLength(codeLength); 49 | List byteList = new LinkedList<>(); 50 | byte[] bytes = iterator.read(codeLength); 51 | String codeStr = iterator.parse2String(bytes); 52 | List cmds = CommandParser.parse(constantPool, codeStr); 53 | code.setCmds(cmds); 54 | for (byte b : bytes) { 55 | byteList.add(b); 56 | } 57 | code.setCode(byteList); 58 | int exceptionTableLength = iterator.readU2ToInt(); 59 | code.setExceptionTableLength(exceptionTableLength); 60 | for (int i = 0; i < exceptionTableLength; ++i) { 61 | // TODO exception table 62 | System.err.println("There is a exception table!"); 63 | } 64 | int attributesCount = iterator.readU2ToInt(); 65 | code.setAttributeLength(attributesCount); 66 | List abstractAttributeInfoList = new LinkedList<>(); 67 | for (int i = 0; i < attributesCount; ++i) { 68 | AbstractAttributeInfo innerAbstractAttributeInfo = create(iterator, constantPool); 69 | abstractAttributeInfoList.add(innerAbstractAttributeInfo); 70 | } 71 | code.setAttributes(abstractAttributeInfoList); 72 | abstractAttributeInfo = code; 73 | break; 74 | case LINE_NUMBER_TABLE: 75 | LineNumberTable lineNumberTable = new LineNumberTable(); 76 | lineNumberTable.setAttributeNameIndex(nameIndex); 77 | lineNumberTable.setAttributeLength(iterator.readU4ToInt()); 78 | int lineNumberTableLength = iterator.readU2ToInt(); 79 | lineNumberTable.setLineNumberTableLength(lineNumberTableLength); 80 | List lineNumberList = new LinkedList<>(); 81 | for (int i = 0; i < lineNumberTableLength; ++ i) { 82 | lineNumberList.add(lineNumberTable.generateLineNumber(iterator)); 83 | } 84 | lineNumberTable.setLineNumberTable(lineNumberList); 85 | abstractAttributeInfo = lineNumberTable; 86 | break; 87 | 88 | case LOCAL_VARIABLE_TABLE: 89 | LocalVariableTable localVariableTable = new LocalVariableTable(); 90 | localVariableTable.setAttributeNameIndex(nameIndex); 91 | int attributeLength = iterator.readU4ToInt(); 92 | localVariableTable.setAttributeNameIndex(attributeLength); 93 | List localVariableList = new LinkedList<>(); 94 | for (int i = 0; i < attributeLength; ++ i) { 95 | localVariableList.add(localVariableTable.generateLocalVariable(iterator)); 96 | } 97 | localVariableTable.setLocalVariableTable(localVariableList); 98 | abstractAttributeInfo = localVariableTable; 99 | break; 100 | 101 | case STACK_MAP_TABLE: 102 | // TODO 103 | System.err.println("STACK_MAP_TABLE"); 104 | break; 105 | 106 | case CONSTANT_VALUE: 107 | // TODO 108 | System.err.println("CONSTANT_VALUE"); 109 | break; 110 | 111 | default: 112 | throw new IllegalArgumentException(content); 113 | } 114 | return abstractAttributeInfo; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/ClassFile.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model; 2 | 3 | import org.destiny.jvm.model.constant.ConstantPool; 4 | import org.destiny.jvm.model.constant.detail.AbstractConstantInfo; 5 | import org.destiny.jvm.model.constant.detail.ConstantUtf8Info; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author 王康 11 | * destinywk@163.com 12 | * ------------------------------------------------------------------ 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2017 Destiny, Org. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2017/8/22 16:38 19 | */ 20 | public class ClassFile { 21 | 22 | private String magicNumber; 23 | private int minorVersion; 24 | private int majorVersion; 25 | private ConstantPool constantPool; 26 | private List accessFlags; 27 | private int thisClass; 28 | private int superClass; 29 | private int interfacesCount; 30 | private List interfaces; 31 | private int fieldsCount; 32 | private List fields; 33 | private int methodCount; 34 | private List methodInfos; 35 | 36 | public MethodInfo getMethod(String name, String descriptorName) { 37 | for (MethodInfo methodInfo : methodInfos) { 38 | AbstractConstantInfo absNameInfo = constantPool.getConstantInfoList().get(methodInfo.getNameIndex()); 39 | if (absNameInfo instanceof ConstantUtf8Info) { 40 | ConstantUtf8Info nameInfo = (ConstantUtf8Info) absNameInfo; 41 | String methodName = new String(nameInfo.getBytes()); 42 | AbstractConstantInfo absDescInfo = constantPool.getConstantInfoList().get(methodInfo.getDescriptorIndex()); 43 | if (absDescInfo instanceof ConstantUtf8Info) { 44 | ConstantUtf8Info descInfo = (ConstantUtf8Info) absDescInfo; 45 | String descName = new String(descInfo.getBytes()); 46 | if (name.equals(methodName) && descriptorName.equals(descName)) { 47 | return methodInfo; 48 | } 49 | } 50 | } 51 | } 52 | return null; 53 | } 54 | 55 | public MethodInfo getMainMethod() { 56 | return getMethod("main", "([Ljava/lang/String;)V"); 57 | } 58 | 59 | public String getMagicNumber() { 60 | return magicNumber; 61 | } 62 | 63 | public void setMagicNumber(String magicNumber) { 64 | this.magicNumber = magicNumber; 65 | } 66 | 67 | public int getMinorVersion() { 68 | return minorVersion; 69 | } 70 | 71 | public void setMinorVersion(int minorVersion) { 72 | this.minorVersion = minorVersion; 73 | } 74 | 75 | public int getMajorVersion() { 76 | return majorVersion; 77 | } 78 | 79 | public void setMajorVersion(int majorVersion) { 80 | this.majorVersion = majorVersion; 81 | } 82 | 83 | public List getAccessFlags() { 84 | return accessFlags; 85 | } 86 | 87 | public void setAccessFlags(List accessFlags) { 88 | this.accessFlags = accessFlags; 89 | } 90 | 91 | public ConstantPool getConstantPool() { 92 | return constantPool; 93 | } 94 | 95 | public void setConstantPool(ConstantPool constantPool) { 96 | this.constantPool = constantPool; 97 | } 98 | 99 | public int getThisClass() { 100 | return thisClass; 101 | } 102 | 103 | public void setThisClass(int thisClass) { 104 | this.thisClass = thisClass; 105 | } 106 | 107 | public int getSuperClass() { 108 | return superClass; 109 | } 110 | 111 | public void setSuperClass(int superClass) { 112 | this.superClass = superClass; 113 | } 114 | 115 | public int getInterfacesCount() { 116 | return interfacesCount; 117 | } 118 | 119 | public void setInterfacesCount(int interfacesCount) { 120 | this.interfacesCount = interfacesCount; 121 | } 122 | 123 | public List getInterfaces() { 124 | return interfaces; 125 | } 126 | 127 | public void setInterfaces(List interfaces) { 128 | this.interfaces = interfaces; 129 | } 130 | 131 | public int getFieldsCount() { 132 | return fieldsCount; 133 | } 134 | 135 | public void setFieldsCount(int fieldsCount) { 136 | this.fieldsCount = fieldsCount; 137 | } 138 | 139 | public List getFields() { 140 | return fields; 141 | } 142 | 143 | public void setFields(List fields) { 144 | this.fields = fields; 145 | } 146 | 147 | public int getMethodCount() { 148 | return methodCount; 149 | } 150 | 151 | public void setMethodCount(int methodCount) { 152 | this.methodCount = methodCount; 153 | } 154 | 155 | public List getMethodInfos() { 156 | return methodInfos; 157 | } 158 | 159 | public void setMethodInfos(List methodInfos) { 160 | this.methodInfos = methodInfos; 161 | } 162 | 163 | @Override 164 | public String toString() { 165 | return "ClassFile{" + 166 | "magicNumber='" + magicNumber + '\'' + 167 | ", minorVersion=" + minorVersion + 168 | ", majorVersion=" + majorVersion + 169 | ", constantPool=" + constantPool + 170 | ", accessFlags=" + accessFlags + 171 | ", thisClass=" + thisClass + 172 | ", superClass=" + superClass + 173 | ", interfacesCount=" + interfacesCount + 174 | ", interfaces=" + interfaces + 175 | ", fieldsCount=" + fieldsCount + 176 | ", fields=" + fields + 177 | ", methodCount=" + methodCount + 178 | ", methodInfos=" + methodInfos + 179 | '}'; 180 | } 181 | 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/command/CommandParser.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.command; 2 | 3 | import org.destiny.jvm.util.CommandIterator; 4 | import org.destiny.jvm.model.constant.ConstantPool; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * @author 王康 11 | * destinywk@163.com 12 | * ------------------------------------------------------------------ 13 | *

14 | * ------------------------------------------------------------------ 15 | * Corpright 2017 Destiny, Org. All rights reserved. 16 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 17 | * @version JDK 1.8.0_101 18 | * @since 2017/8/22 16:38 19 | */ 20 | public class CommandParser { 21 | 22 | public static final String ACONST_NULL = "01"; 23 | public static final String NEW_OBJECT = "BB"; 24 | public static final String LSTORE = "37"; 25 | public static final String INVOKESPECIAL = "B7"; 26 | public static final String INVOKEVIRTUAL = "B6"; 27 | public static final String GETFIELD = "B4"; 28 | public static final String PUTFIELD = "B5"; 29 | public static final String GETSTATIC = "B2"; 30 | public static final String LDC = "12"; 31 | public static final String DUP = "59"; 32 | public static final String BIPUSH = "10"; 33 | public static final String ALOAD_0 = "2A"; 34 | public static final String ALOAD_1 = "2B"; 35 | public static final String ALOAD_2 = "2C"; 36 | public static final String ILOAD = "15"; 37 | public static final String ILOAD_1 = "1B"; 38 | public static final String ILOAD_2 = "1C"; 39 | public static final String ILOAD_3 = "1D"; 40 | public static final String FLOAD_3 = "25"; 41 | 42 | public static final String ATRTURN = "B0"; 43 | public static final String VOIDRETURN = "B1"; 44 | public static final String IRETURN = "AC"; 45 | public static final String FRETURN = "AE"; 46 | 47 | public static final String ASTORE_1 = "4C"; 48 | public static final String IF_ICMP_GE = "A2"; 49 | public static final String IF_ICMPLE = "A4"; 50 | public static final String GOTO_NO_CONDITION = "A7"; 51 | public static final String ICONST_0 = "03"; 52 | public static final String ICONST_1 = "04"; 53 | public static final String ISTORE_1 = "3C"; 54 | public static final String ISTORE_2 = "3D"; 55 | public static final String IADD = "60"; 56 | public static final String IINC = "84"; 57 | 58 | 59 | public static List parse(ConstantPool constantPool, String codes) { 60 | codes = codes.toUpperCase(); 61 | List cmdList = new LinkedList<>(); 62 | CommandIterator iterator = new CommandIterator(codes); 63 | while (iterator.hasNext()) { 64 | String opCode = iterator.next2CharAsString(); 65 | 66 | ByteCodeCmd cmd = null; 67 | switch (opCode) { 68 | case NEW_OBJECT: 69 | cmd = new NewObjectCmd(constantPool, opCode, iterator.next2CharAsInt(), iterator.next2CharAsInt()); 70 | break; 71 | case INVOKESPECIAL: 72 | cmd = new InvokeSpecialCmd(constantPool, opCode, iterator.next2CharAsInt(), iterator.next2CharAsInt()); 73 | break; 74 | case INVOKEVIRTUAL: 75 | cmd = new InvokeVirtualCmd(constantPool, opCode, iterator.next2CharAsInt(), iterator.next2CharAsInt()); 76 | break; 77 | case GETFIELD: 78 | cmd = new GetFieldCmd(constantPool, opCode, iterator.next2CharAsInt(), iterator.next2CharAsInt()); 79 | break; 80 | case GETSTATIC: 81 | cmd = new GetStaticFieldCmd(constantPool, opCode, iterator.next2CharAsInt(), iterator.next2CharAsInt()); 82 | break; 83 | case PUTFIELD: 84 | cmd = new PutFieldCmd(constantPool, opCode, iterator.next2CharAsInt(), iterator.next2CharAsInt()); 85 | break; 86 | case LDC: 87 | cmd = new LdcCmd(constantPool, opCode, iterator.next2CharAsInt()); 88 | break; 89 | case BIPUSH: 90 | cmd = new BipushCmd(constantPool, opCode, iterator.next2CharAsInt()); 91 | break; 92 | case DUP: 93 | cmd = new DupCmd(constantPool, opCode); 94 | break; 95 | case ALOAD_0: 96 | cmd = new Aload0Cmd(constantPool, opCode); 97 | break; 98 | case ALOAD_1: 99 | cmd = new Aload1Cmd(constantPool, opCode); 100 | break; 101 | case ALOAD_2: 102 | cmd = new Aload2Cmd(constantPool, opCode); 103 | break; 104 | case ILOAD: 105 | cmd = new IloadCmd(constantPool, opCode); 106 | break; 107 | case ILOAD_1: 108 | cmd = new Iload1Cmd(constantPool, opCode); 109 | break; 110 | case ILOAD_2: 111 | cmd = new Iload2Cmd(constantPool, opCode); 112 | break; 113 | case ILOAD_3: 114 | cmd = new Iload3Cmd(constantPool, opCode); 115 | break; 116 | case FLOAD_3: 117 | cmd = new Fload3Cmd(constantPool, opCode); 118 | break; 119 | case VOIDRETURN: 120 | cmd = new VoidReturnCmd(constantPool, opCode); 121 | break; 122 | case ASTORE_1: 123 | cmd = new AStore1Cmd(constantPool, opCode); 124 | break; 125 | default: 126 | throw new RuntimeException("the java instruction " + opCode + " is invalid"); 127 | } 128 | cmdList.add(cmd); 129 | calcuateOffset(cmdList); 130 | } 131 | return cmdList; 132 | } 133 | 134 | private static void calcuateOffset(List codeCmds) { 135 | int offset = 0; 136 | for (ByteCodeCmd byteCodeCmd : codeCmds) { 137 | byteCodeCmd.setOffset(offset); 138 | offset += byteCodeCmd.getLength(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/org/destiny/jvm/model/attr/Code.java: -------------------------------------------------------------------------------- 1 | package org.destiny.jvm.model.attr; 2 | 3 | import org.destiny.jvm.model.AbstractAttributeInfo; 4 | import org.destiny.jvm.model.command.ByteCodeCmd; 5 | import org.destiny.jvm.model.command.CommandParser; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | /** 11 | * @author 王康 12 | * destinywk@163.com 13 | * ------------------------------------------------------------------ 14 | *

15 | * 变长属性,位于method_info中 16 | *

17 | * ------------------------------------------------------------------ 18 | * Corpright 2017 Destiny, Org. All rights reserved. 19 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 20 | * @version JDK 1.8.0_101 21 | * @since 2017/8/22 16:38 22 | */ 23 | public class Code extends AbstractAttributeInfo { 24 | 25 | private int attributeNameIndex; // 指向常量池,应该是UTF8Info,值为Code 26 | private int attributeLength; // 属性长度,不包括开始的6个字节 27 | private int maxStack; // 操作数栈的最大深度 28 | private int maxLocals; // 最大局部变量表个数 29 | private int codeLength; // 该方法的字节码长度 30 | private List code; // 真正的字节码 31 | private List cmds; // 字节码指令集 32 | private int exceptionTableLength; // 异常表的长度 33 | private List exceptionTable; // 异常列表 34 | private int attributesCount; // 嵌套属性的数量 35 | private List attributes; // 嵌套属性的列表 36 | 37 | /** 38 | * 字节码指令转化器 39 | */ 40 | private CommandParser commandParser; 41 | 42 | public int getAttributeNameIndex() { 43 | return attributeNameIndex; 44 | } 45 | 46 | public void setAttributeNameIndex(int attributeNameIndex) { 47 | this.attributeNameIndex = attributeNameIndex; 48 | } 49 | 50 | public int getAttributeLength() { 51 | return attributeLength; 52 | } 53 | 54 | public void setAttributeLength(int attributeLength) { 55 | this.attributeLength = attributeLength; 56 | } 57 | 58 | public int getMaxStack() { 59 | return maxStack; 60 | } 61 | 62 | public void setMaxStack(int maxStack) { 63 | this.maxStack = maxStack; 64 | } 65 | 66 | public int getMaxLocals() { 67 | return maxLocals; 68 | } 69 | 70 | public void setMaxLocals(int maxLocals) { 71 | this.maxLocals = maxLocals; 72 | } 73 | 74 | public int getCodeLength() { 75 | return codeLength; 76 | } 77 | 78 | public void setCodeLength(int codeLength) { 79 | this.codeLength = codeLength; 80 | } 81 | 82 | public List getCode() { 83 | return code; 84 | } 85 | 86 | public void setCode(List code) { 87 | this.code = code; 88 | } 89 | 90 | public int getExceptionTableLength() { 91 | return exceptionTableLength; 92 | } 93 | 94 | public void setExceptionTableLength(int exceptionTableLength) { 95 | this.exceptionTableLength = exceptionTableLength; 96 | } 97 | 98 | public List getExceptionTable() { 99 | return exceptionTable; 100 | } 101 | 102 | public void setExceptionTable(List exceptionTable) { 103 | this.exceptionTable = exceptionTable; 104 | } 105 | 106 | public int getAttributesCount() { 107 | return attributesCount; 108 | } 109 | 110 | public void setAttributesCount(int attributesCount) { 111 | this.attributesCount = attributesCount; 112 | } 113 | 114 | public List getAttributes() { 115 | return attributes; 116 | } 117 | 118 | public void setAttributes(List attributes) { 119 | this.attributes = attributes; 120 | } 121 | 122 | public List getCmds() { 123 | return cmds; 124 | } 125 | 126 | public void setCmds(List cmds) { 127 | this.cmds = cmds; 128 | } 129 | 130 | public CommandParser getCommandParser() { 131 | return commandParser; 132 | } 133 | 134 | public void setCommandParser(CommandParser commandParser) { 135 | this.commandParser = commandParser; 136 | } 137 | 138 | @Override 139 | public String toString() { 140 | return "Code{" + 141 | "attributeNameIndex=" + attributeNameIndex + 142 | ", attributeLength=" + attributeLength + 143 | ", maxStack=" + maxStack + 144 | ", maxLocals=" + maxLocals + 145 | ", codeLength=" + codeLength + 146 | ", code=" + code + 147 | ", cmds=" + cmds + 148 | ", exceptionTableLength=" + exceptionTableLength + 149 | ", exceptionTable=" + exceptionTable + 150 | ", attributesCount=" + attributesCount + 151 | ", attributes=" + attributes + 152 | ", commandParser=" + commandParser + 153 | '}'; 154 | } 155 | 156 | /** 157 | * @author 王康 158 | * destinywk@163.com 159 | * ------------------------------------------------------------------ 160 | *

161 | * ------------------------------------------------------------------ 162 | * Corpright 2017 Destiny, Org. All rights reserved. 163 | * NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 164 | * @version JDK 1.8.0_101 165 | * @since 2017/8/22 16:38 166 | */ 167 | public static class ExceptionInfo { 168 | private int startPc; // 异常处理器在code[]中生效的起始位置 169 | private int endPc; // 异常处理器在code[]中生效的结束位置 170 | private int handlerPc; // 表示一个异常处理器的起点 171 | private int catchType; // 对常量池的索引,成员必须是CONSTANT_Class_info,表示当前异常处理器需要捕捉的异常类型 172 | 173 | public int getStartPc() { 174 | return startPc; 175 | } 176 | 177 | public void setStartPc(int startPc) { 178 | this.startPc = startPc; 179 | } 180 | 181 | public int getEndPc() { 182 | return endPc; 183 | } 184 | 185 | public void setEndPc(int endPc) { 186 | this.endPc = endPc; 187 | } 188 | 189 | public int getHandlerPc() { 190 | return handlerPc; 191 | } 192 | 193 | public void setHandlerPc(int handlerPc) { 194 | this.handlerPc = handlerPc; 195 | } 196 | 197 | public int getCatchType() { 198 | return catchType; 199 | } 200 | 201 | public void setCatchType(int catchType) { 202 | this.catchType = catchType; 203 | } 204 | 205 | @Override 206 | public String toString() { 207 | return "ExceptionInfo{" + 208 | "startPc=" + startPc + 209 | ", endPc=" + endPc + 210 | ", handlerPc=" + handlerPc + 211 | ", catchType=" + catchType + 212 | '}'; 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 自学《深入理解Java虚拟机》和《Java虚拟机规范》之后,自己对 `JVM` 实现的尝试。 3 | 4 | 目标计划: 5 | - 实现 `classpath` 的设置 6 | - 实现对 `.class` 文件的解析和处理 7 | - 实现对 `method` 字段中 `code` 属性的执行,模拟 JVM 执行引擎 8 | 9 | 系统类图: 10 | - 11 | ![](http://on-img.com/chart_image/5aacfabfe4b0262b8b3f52d6.png) 12 | 13 | - `ClassFileLoader` 作为系统的入口,负责设置 `classpath` 以及装载字节码文件。 14 | - `ClassFileAnalyser` 负责读取并解析字节码文件。 15 | - `ByteCodeIterator` 采用迭代器模式,将字节码文件传入进行初始化之后,对外界屏蔽底层字节操作,返回特定长度的整型或者字符串。 16 | - `ClassFile` 包含 `ConstantPool`、`AccessFlag`、`FieldInfo`、`MethodInfo` 等属性,其中 `ConstantPool` 和 `Attribute` 依据类型的不同,由工厂类进行创建。 17 | - `ByteCodeParse` 使用命令模式,将 JVM 执行引擎可识别的字节码命令抽象为 `ByteCodeCmd` 接口的实现类,并再次抽象出 `NoOperandCmd`、`OneOperandCmd`和`TwoOperandCmd`。 18 | 19 | # 1. class类文件结构 20 | Class文件是一组以8字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有任何分隔符。 21 | 22 | - 无符号数属于基本的数据类型,以`u1`、`u2`、`u4`、`u8`来分别代表1个字节、2个字节、4个字节、8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码组成的字符串值。 23 | - 表是有多个无符号数或者其他表作为数据项构成的复合数据结构,所有表都习惯性的以`_info`结尾。表用于描述有层次关系的复合结构的数据。 24 | 25 | ###
Class文件格式
26 | 27 |
28 | 29 | 类型 | 名称 | 数量 30 | :-: | :-: | :-: 31 | u4 | magic | 1 32 | u2 | minor_version | 1 33 | u2 | major_version | 1 34 | u2 | constant_pool_count | 1 35 | cp_info | constant_pool | constant_pool_count - 1 36 | u2 | access_flags | 1 37 | u2 | this_class | 1 38 | u2 | super_class | 1 39 | u2 | interfaces_count | 1 40 | u2 | interfaces | interfaces_count 41 | u2 | fields_count | 1 42 | field_info | fields | fields_count 43 | u2 | methods_count | 1 44 | method_info | methods | methods_count 45 | u2 | attributes_count | 1 46 | attribute_info | attributes | attributes_count - 1 47 | 48 |
49 | 50 | 当需要描述同一类型但数量不定的时候,都会通过一个前置容器计数器+若干个数据项的形式。 51 | 52 | ## 1.1 魔数与版本号 53 | 54 | 使用魔数来确定这个文件是否为一个能被JVM接受的Class文件。 55 | 值必须为Oxcafebabe。 56 | 57 | ## 1.2 常量池 58 | 59 | Class文件之中的资源仓库 60 | 61 | 常量池中主要存放字面量和符号引用。 62 | - 字面量即文本字符串、final常量等 63 | - 符号引用包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符 64 | 65 | Java代码在进行编译的时候,没有连接这一步骤,而是由JVM加载Class文件的时候进行动态连接。因此在Class文件中不会保存各个方法、字段的最终内存布局信息。在虚拟机运行的时候,需要从常量池获得对应的符号引用,再在类创建或者运行的时候解析、翻译到具体的内存地址中。 66 | 67 | ###
常量池的项目类型
68 |
69 | 70 | 类型 | 标志 | 描述 71 | :-: | :-: | :-: 72 | CONSTANT_Utf8_info | 1 | UTF-8编码的字符串 73 | CONSTANT_Integer_info | 3 | 整型字面量 74 | CONSTANT_Float_info | 4 | 浮点型字面量 75 | CONSTANT_Long_info | 5 | 长整型字面量 76 | CONSTANT_Double_info | 6 | 双精度浮点型字面量 77 | CONSTANT_Class_info | 7 | 类或接口的符号引用 78 | CONSTANT_String_info | 8 | 字符串类型字面量 79 | CONSTANT_Field_info | 9 | 字段的符号引用 80 | CONSTANT_Methodref_info | 10 | 方法的符号引用 81 | CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 82 | CONSTANT_NameAndType_info | 12 | 字段或方法的部分符号引用 83 | CONSTANT_MethodHandle_info | 15 | 方法句柄 84 | CONSTANT_MethodType_info | 16 | 方法类型 85 | CONSTANT_Method_info | 18 | 一个动态方法调用点 86 | 87 |
88 | 89 | ###
常量池的14中常量项的结构总览
90 | ![](http://oetw0yrii.bkt.clouddn.com/18-3-11/26496686.jpg) 91 | 92 | ## 1.3 访问标志 93 | 94 | | 标志名称 | 标志值 | 含义 95 | | :-: | :-: | :-: | 96 | ACC_PUBLIC | 0x0001 | 是否public 97 | ACC_FINAL | 0x0010 | 98 | ACC_SUPER | 0x0020 99 | ACC_INTERFACE | 0x0200 100 | ACC_ABSTRACT | 0x0400 101 | ACC_SYNTHETIC | 0x1000 102 | ANNOTATION | 0x2000 103 | ENUM | 0x4000 104 | 105 | ## 1.4 类索引、父类索引与接口索引集合 106 | 107 | - 类索引是一个u2类型的数据,用于确定该类的全限定名 108 | - 父类索引是一个u2类型的数据,用于确定父类的全限定名 109 | - 接口索引集合是一组u2类型的数据的集合,用来描述这个类实现了哪些接口,这些被实现的接口将按`implements`语句后的接口顺序从左到右排列早接口索引集合中 110 | 111 | Class文件中由这三项数据来确定这个类的继承关系。 112 | 113 | ####
类索引查找全限定名的过程
114 | 115 | ![](http://oetw0yrii.bkt.clouddn.com/18-3-11/4410654.jpg) 116 | 117 | ## 1.5 字段表集合 118 | 119 | 字段表,用于描述类或接口中声明的变量。 120 | 121 | 字段包括类级变量以及实例级变量,但不包括方法中声明的变量。 122 | 123 | Java中描述一个字段需要的信息: 124 | - 访问限制符 125 | - 实例变量or类变量 126 | - 可变性 127 | - 并发可见性 128 | - 是否可被序列化 129 | - 数据类型 130 | - 字段名称 131 | 132 | 各个修饰符都是都是bool值,而字段名、数据类型都是不固定的,只能引用常量池中的常量来描述。 133 | 134 | ####
字段表结构
135 | ``` 136 | field_info { 137 | u2 access_flags; 138 | u2 name_index; // 对常量池的引用,代表字段的简单名称,即没有类型和参数修饰符的名称 139 | u2 descriptor_index; // 对常量池的引用,代表字段的描述符,即字段的数据类型 140 | u2 attributes_count; 141 | attribute_info attribute[attributes_count]; 142 | } 143 | ``` 144 | 145 | 146 | ####
全限定名、简单名称和描述符
147 | 名称 | 含义 148 | :-: | :-: 149 | 全限定名 | 把类全限定名中的"/"换成".",为了使多个连续的全限定名之间不产生混淆,在使用的时候最后一般会加入一个";" 150 | 简单名称 | 没有参数类型和参数修饰的字段和方法的名称 151 | 描述符 | 描述字段的数据类型、方法的参数列表和返回值 152 | 153 | ####
描述符标识字符含义
154 | 155 | 标识字符 | 含义 156 | :-: | :-: 157 | B | byte 158 | C | char 159 | D | double 160 | F | float 161 | F | float 162 | I | int 163 | J | long 164 | S | short 165 | Z | boolean 166 | V | void 167 | L | 对象,如Ljava/lang/Object; 168 | 169 | 字段表集合中不会列出从超类或者父类中集成而来的字段,但有可能列出原本代码中不存在的字段,譬如在内部类中为了保持对外部类的访问,会自动添加指向外部类实例的字段。 170 | 171 | ## 1.6 方法表集合 172 | ``` 173 | method_info { 174 | u2 access_flags; 175 | u2 name_index; // 对常量池的引用,代表方法的简单名称,即没有类型和参数修饰符的名称 176 | u2 descriptor_index; // 对常量池的引用,代表方法的描述符,即字段的数据类型 177 | u2 attributes_count; 178 | attribute_info attribute[attributes_count]; 179 | } 180 | ``` 181 | 182 | ## 1.7 属性表集合 183 | 184 | 在Class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息。 185 | 186 | 属性表并不要求各个属性表有严格的顺序,只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,JVM运行时会忽略掉不识别的属性。 187 | 188 | ####
JVM规范预定义的属性
189 | 190 | 属性名 | 使用位置 | 含义 191 | :-: | :-: | :-: | 192 | Code | 方法表 | Java代码编译成的字节码指令 193 | ConstantValue | 字段表 | final关键字定义的常量值 194 | Deprecated | 类、方法表、字段表 | 被声明为Deprecated 195 | Exceptions | 方法表 | 方法抛出的异常 196 | EnclosingMethod | 类文件 | 仅当一个类作为局部类或者匿名类时才拥有这个属性,这个属性用于标识这个类所在的外围方法 197 | InnerClass | 类文件 | 内部类列表 198 | LineNumberTable | Code属性 | Java源码的行号与字节码指令的对应关系 199 | LocalVariableTable | Code属性 | 方法局部变量的描述 200 | StackMapTable | Code属性 | JDK1.6中新增的属性,供新的类型检查验证器检车和处理目标方法的局部变量和操作数栈所需要的类型是否匹配 201 | Signature | 类、方法表、字段表 | JDK1.5新增的属性,用于支持泛型情况下的方法签名,任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量或参数化类型,则该属性会为它记录泛型签名信息。为了避免泛型信息被擦除后导致签名混乱,需要这个属性记录泛型中的相关信息。 202 | SourceFile | 类文件 | 记录源文件名称 203 | SourceDebugExtension | 类文件 | JDK1.6中新增的属性,用于存储额外的调试信息。 204 | LocalVariableTypeTable | 类 | JDK1.5中新增的属性,使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 205 | 206 | 207 | 对于每个属性,它的名称都需要从常量池中引用一个`CONSTANT_Utf8_info`类型的常量来表示,而属性值的结构则完全是自定义的,只需要通过一个U4长度的属性去说明属性值所占用的位数即可 208 | 209 | ``` 210 | attribute_info { 211 | u2 attribute_name_index; 212 | u4 attribute_length; 213 | u1 info[attribute_length]; 214 | } 215 | ``` 216 | 217 | ### 1.6.1 Code属性 218 | Java程序方法体中的代码经过编译后,最终变为字节码指令存储在Code属性内。 219 | 220 | 类型 | 名称 | 数量 221 | :-: | :-: | :-: 222 | u2 | attribute_name_index | 1 223 | u4 | attribute_length | 1 224 | u2 | max_stack | 1 225 | u2 | max_locals | 1 226 | u4 | code_length | 1 227 | u1 | code | code_length 228 | u2 | exception_table_length | 1 229 | exception_info | exception_table | exception_table_length 230 | u2 | attributes_count | 1 231 | attribute_info | attributes | attributes_count 232 | 233 | # 2. 字节码指令简介 234 | 235 | JVM的指令由一个字节长度的操作码以及跟随其后的零至多个操作数构成。 236 | 237 | Class文件放弃了编译后代码的操作数长度对齐,因此实际的操作方式为: 238 | 239 | ```java 240 | do { 241 | // 自动计算PC寄存器的值+1 242 | // 根据PC寄存器指示的位置,从字节码中读取操作码 243 | if (字节码存在操作数) { 244 | // 从字节码中读取操作数 245 | } 246 | // 执行操作码所定义的操作 247 | } while(字节码长度 > 0); 248 | ``` 249 | 250 | ## 2.1 字节码与数据类型 251 | 252 | 大多数指令都包含了其操作所对应的数据类型信息 253 | iload指令用于从局部变量表中加载int型的数据到操作数栈中 254 | 255 | 256 | 257 | 258 | ## 2.2 加载和存储指令 259 | 260 | 加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输 261 | 262 | 263 | - [ ] 将一个局部变量加载到操作栈: `iload`、`iload_`、`lload`、`lload_`、`fload`、`fload_`、`dload`、`dload_`、`aload`、`aload_` 264 | - [ ] 将一个数值从操作数栈存储到局部变量表:`istore`、`istore_`、`lstore`、`lstore_`、`fstore`、`fstore_`、`dstore`、`dstore_`、`astore`、`astore_` 265 | - [ ] 将一个常量加载到操作数栈:`bipush`、`sipush`、`ldc`、`ldc_w`、`ldc2_w`、`aconst_null`、`iconst_m l`、`iconst_`、`lconst`、`fconst_`、`dconst_` 266 | 267 | ## 2.3 运算指令 268 | 269 | 运算或算数指令用于到两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作数栈顶。 270 | 271 | 具体的运算包括: 272 | - 加法指令 273 | - 减法指令 274 | - 乘法指令 275 | - 除法指令 276 | - 求余指令 277 | - 取反指令 278 | - 位移指令 279 | - 按位或指令 280 | - 按为与指令 281 | - 按位异或指令 282 | - 局部变量自增指令 283 | - 比较指令 284 | 285 | ## 2.4 类型转换指令 286 | 287 | 将两种不同的数值类型进行相互转换。 288 | 289 | ## 2.5 对象创建于访问指令 290 | 291 | 对象创建后,就可以通过对象访问指令获取对象实例或者数组实例中的字段或者数组元素 292 | 293 | ## 2.6 操作数栈管理指令 294 | 295 | 类似于操作一个普通数据结构中的堆栈 296 | 297 | ## 2.7 控制转移指令 298 | 299 | 控制转移指令可以让Java虚拟机有条件或无条件地从指定的位置指令的下一条指令继续执行程序。 300 | 可以认为控制转移指令就是在有条件或无条件地修改PC寄存器的值。 301 | 302 | ## 2.8 方法调用和返回指令 303 | 304 | - invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派。 305 | - invokeinterface指令用于调用接口方法,会在运行时搜索一个实现了这个接口的方法的对象 306 | - invokespecial指令用于调用一些需要特殊处理的实例方法,包括初始化方法、私有方法和父类方法 307 | - invokestatic指令用于调用类方法 308 | - invokedynamic指令用于在运行时动态解析出调用点限定符所引用的方法 309 | 310 | ## 2.9 异常处理指令 311 | 312 | 在Java中显式抛出异常的操作都由athrow指令来实现 313 | 而在JVM中,处理异常(catch)不是由字节码指令来实现的,而是采用异常表来完成的。 314 | --------------------------------------------------------------------------------