mirror of
https://github.com/FranzHaidnor/haidnorJVM.git
synced 2026-03-13 21:43:42 +08:00
update README.md
This commit is contained in:
99
README.md
99
README.md
@@ -11,25 +11,36 @@
|
|||||||
* [Apache Commons CLI](https://commons.apache.org/proper/commons-cli/)
|
* [Apache Commons CLI](https://commons.apache.org/proper/commons-cli/)
|
||||||
|
|
||||||
# 实现功能
|
# 实现功能
|
||||||
* 支持基本数据类型数学运算
|
* 实现了 99% 的 JVM 字节码指令。参照 JVM 字节码规范实现 [The Java Virtual Machine Instruction Set](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html)
|
||||||
* 支持循环、条件结构代码
|
* 支持算数运算符 (`+`,`-`,`*`,`^`,`%`,`++`,`--`)
|
||||||
* switch 语法(开发中...)
|
* 支持关系运算符 (`==`,`!=`,`>`,`<`,`>=`,`<=`)
|
||||||
* 支持创建对象,访问对象
|
* 支持位运算符 (`&`,`|`,`^`,`~`,`<<`,`>>`,`>>>`)
|
||||||
|
* 支持赋值运算符 (`=`,`+=`,`-=`,`*=`,`%=`,`<<=`,`>>=`,`&=`,`^=`,`|=`)
|
||||||
|
* 支持 instanceof 运算符
|
||||||
|
* 支持循环结构代码 (`while`,`do...while`,`for`,`foreach`)
|
||||||
|
* 支持条件结构代码 (`if`,`if...else`,`if...else if`)
|
||||||
|
* 支出创建自定义类
|
||||||
|
* 支持创建对象、访问对象
|
||||||
|
* 支持抽象类
|
||||||
* 支持多态继承、接口
|
* 支持多态继承、接口
|
||||||
* 支持反射
|
|
||||||
* 支持 JDK 中自带的 Java 类
|
|
||||||
* 支持访问静态方法
|
* 支持访问静态方法
|
||||||
|
* 支持访问对象方法
|
||||||
|
* 支持 JDK 中自带的 Java 类
|
||||||
|
* 支持反射
|
||||||
* 支持异常
|
* 支持异常
|
||||||
* lambda 表达式(开发中...)
|
* 枚举 (开发中...)
|
||||||
|
* switch 语法 (开发中...)
|
||||||
|
* lambda 表达式 (开发中...)
|
||||||
|
|
||||||
# 局限性
|
# 局限性
|
||||||
* 不支持多线程
|
* 不支持多线程
|
||||||
* 不支持多维数组
|
* 不支持多维数组
|
||||||
* 垃圾回收依靠宿主 JVM
|
* 暂无双亲委派机制实现
|
||||||
|
* 无垃圾收集器实现。垃圾回收依靠宿主 JVM
|
||||||
|
|
||||||
# 快速体验
|
# 快速体验
|
||||||
## 你需要准备什么
|
## 你需要准备什么
|
||||||
1. 集成开发环境 (IDE)。你可以选择包括 IntelliJ IDEA、Spring Tools、Visual Studio Code 或 Eclipse 等等
|
1. 集成开发环境 (IDE)。你可以选择包括 IntelliJ IDEA、Visual Studio Code 或 Eclipse 等等
|
||||||
2. JDK 17。并配置 JAVA_HOME
|
2. JDK 17。并配置 JAVA_HOME
|
||||||
3. JDK 8。haidnorJVM 的主要目标是运行 Java8 本版的字节码文件。(但 haidnorJVM 没有强制要求字节码文件是 Java8 版本)
|
3. JDK 8。haidnorJVM 的主要目标是运行 Java8 本版的字节码文件。(但 haidnorJVM 没有强制要求字节码文件是 Java8 版本)
|
||||||
4. Maven
|
4. Maven
|
||||||
@@ -39,7 +50,7 @@
|
|||||||
在 `resources\simplelogger.properties` 文件中修改日志输出级别,一般使用 `debug`、`info`
|
在 `resources\simplelogger.properties` 文件中修改日志输出级别,一般使用 `debug`、`info`
|
||||||
|
|
||||||
* 配置 info 级别将不会看到任何 haidnorJVM 内部运行信息
|
* 配置 info 级别将不会看到任何 haidnorJVM 内部运行信息
|
||||||
* 配置 debug 级别下运行将会非常友好的输出 JVM 正在执行的栈信息 (例如使用 haidnorJVM 运行以下代码)
|
* 配置 debug 级别下运行将会非常友好的输出 JVM 正在执行的栈信息
|
||||||
```java
|
```java
|
||||||
public class Demo5 {
|
public class Demo5 {
|
||||||
|
|
||||||
@@ -63,42 +74,72 @@ public class Demo5 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
每一个 `匚` 结构图形,都表示一个 JVM 线程栈中的栈帧
|
||||||

|

|
||||||
|
|
||||||
### 配置 rt.jar
|
### 配置 rt.jar 路径
|
||||||
修改 `haidnorJVM.properties` 文件中的内容。配置 rt.jar 的绝对路径,例如`rt.jar=D:/Program Files/Java/jdk1.8.0_361/jre/lib/rt.jar`
|
修改 `haidnorJVM.properties` 文件中的内容。配置 rt.jar 的绝对路径,例如`rt.jar=D:/Program Files/Java/jdk1.8.0_361/jre/lib/rt.jar`
|
||||||
|
|
||||||
## 运行单元测试用例
|
## 运行单元测试用例
|
||||||
打开 test 目录下的 `haidnor.jvm.test.TestJVM` 类文件。 这是 haidnorJVM 功能的主要测试类。 里面的测试方法可以解析并加载一些class字节码文件。
|
在 IDE 中打开项目中 test 目录下的 `haidnor.jvm.test.TestJVM.java` 文件。 这是 haidnorJVM 的主要测试类, 里面的测试方法可以解析加载运行 .class 字节码文件。
|
||||||
```java
|
```java
|
||||||
@Test
|
public class TestJVM {
|
||||||
public void test() throws Exception {
|
/**
|
||||||
runMainClass(HelloWorld.class);
|
* haidnorJVM 会加载 HelloWorld.java 在 target 目录下的编译后的字节码文件,然后运行其中的 `main(String[] args)` 方法。
|
||||||
|
* 你可以使用打断点的方式看到 haidnorJVM 是如何解释运行 Java 字节码的。
|
||||||
|
* 值得注意的是,这种方式编译运行的字节码文件是基于 java17 版本的。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
runMainClass(HelloWorld.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
例如以上代码会加载 HelloWorld.class 类在 target 目录下的字节码文件,然后使用 haidnorJVM 运行其中的 main 函数。你可以使用打断点的方式看到 haidnorJVM 是如何解释运行 Java 字节码的。
|
|
||||||
值得注意的是,这种方式编译运行的字节码文件是基于 java17 版本的。
|
|
||||||
|
|
||||||
## 运行 .class 文件
|
## 运行 .class 文件
|
||||||
1. 使用 maven 命令将 haidnorJVM 编译打包,得到 `haidnorJVM.jar` 文件
|
1. 使用 maven 命令将 haidnorJVM 编译打包,得到 `haidnorJVM.jar` 文件
|
||||||
2. 编写一个简单的程序,例如以下代码
|
2. 编写一个简单的程序,例如以下代码
|
||||||
```java
|
```java
|
||||||
public class HelloWorld {
|
public class HelloWorld {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("HelloWorld");
|
System.out.println("HelloWorld");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
}
|
||||||
3. 编译代码,得到 HelloWorld.class 文件。(推荐使用 JDK8 进行编译)
|
```
|
||||||
4. 使用 haidnorJVM 运行程序。执行命令 `java -jar haidnorJVM.jar -class R:\HelloWorld.class`。注意需要 class 文件的绝对路径
|
3. 编译代码,得到 HelloWorld.class 文件 (推荐使用 JDK8 进行编译)
|
||||||
|
4. 使用 haidnorJVM 运行程序。执行命令 `java -jar haidnorJVM.jar -class R:\HelloWorld.class`。注意! 需要 class 文件的绝对路径
|
||||||
|
|
||||||
## 运行 .jar 文件
|
## 运行 .jar 文件
|
||||||
1. 使用 maven 命令将 haidnorJVM 编译打包,得到 `haidnorJVM.jar` 文件
|
1. 使用 maven 命令将 haidnorJVM 编译打包,得到 `haidnorJVM.jar` 文件
|
||||||
2. 编写一个 java 项目编译打包成 .jar 文件,例如 demo.jar。要求 .jar 文件中的 META-INF/MANIFEST.MF 文件内有 `Main-Class` 属性 (含有 public static void main(String[] args) 方法的主类信息)
|
2. 编写一个 java 项目编译打包成 .jar 文件,例如 demo.jar。要求 .jar 文件中的 META-INF/MANIFEST.MF 文件内有 `Main-Class` 属性 (含有 `public static void main(String[] args)` 方法的主类信息)
|
||||||
3. 使用 haidnorJVM 运行程序。执行命令 `java -jar haidnorJVM.jar -class R:\demo.jar`。注意需要 jar 文件的绝对路径
|
3. 使用 haidnorJVM 运行程序。执行命令 `java -jar haidnorJVM.jar -class R:\demo.jar`。注意! 需要 jar 文件的绝对路径
|
||||||
|
|
||||||
# 计划
|
# 存在的问题
|
||||||
目前运行 JDK 自带的类的方案是使用反射解决,并存在一些bug,计划将来完全不依赖反射运行。
|
由于 haidnorJVM 目前运行 JDK 自带的类是使用反射解决的,因此 haidnorJVM 使用 JDK17 运行部分 JDK 自带的类时会存在一些问题,例如运行以下代码将会抛出异常
|
||||||
|
```java
|
||||||
|
public class Demo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<Integer> list = List.of(1, 2, 3, 4, 5);
|
||||||
|
list.add(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
java.lang.reflect.InaccessibleObjectException: Unable to make public boolean java.util.ImmutableCollections$AbstractImmutableCollection.add(java.lang.Object) accessible: module java.base does not "opens java.util" to unnamed module @18769467
|
||||||
|
|
||||||
|
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
|
||||||
|
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
|
||||||
|
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
|
||||||
|
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
|
||||||
|
```
|
||||||
|
它表示尝试通过反射来访问一个方法或字段,但该方法或字段的可访问性限制导致无法访问。
|
||||||
|
|
||||||
|
这个限制通常是由于 Java 模块系统引起的。模块系统允许将代码划分为独立的模块,
|
||||||
|
并控制模块之间的访问权限。以上异常的原因是 module java.base does not "opens java.util" to unnamed module,也就是说 java.base 模块没有向未命名模块开放 java.util 包
|
||||||
|
|
||||||
|
**解决方法:**
|
||||||
|
启动 haidnorJVM 时添加 JVM 参数 `--add-opens java.base/java.util=ALL-UNNAMED` 绕过访问性限制
|
||||||
|
|
||||||
# 联系作者
|
# 联系作者
|
||||||

|

|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import haidnor.jvm.runtime.StackValue;
|
|||||||
import haidnor.jvm.util.CodeStream;
|
import haidnor.jvm.util.CodeStream;
|
||||||
|
|
||||||
public class IFEQ extends Instruction {
|
public class IFEQ extends Instruction {
|
||||||
/**
|
|
||||||
* 下次再执行的偏移量
|
|
||||||
*/
|
|
||||||
private final int offSet;
|
private final int offSet;
|
||||||
|
|
||||||
public IFEQ(CodeStream codeStream) {
|
public IFEQ(CodeStream codeStream) {
|
||||||
|
|||||||
@@ -19,11 +19,20 @@ public class IFNE extends Instruction {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(Frame frame) {
|
public void execute(Frame frame) {
|
||||||
StackValue v1 = frame.pop();
|
StackValue v1 = frame.pop();
|
||||||
if ((int) v1.getValue() != 0) {
|
if (v1.getValue() instanceof Boolean) {
|
||||||
super.setOffSet(offSet);
|
if (((boolean) v1.getValue())) {
|
||||||
|
super.setOffSet(offSet);
|
||||||
|
} else {
|
||||||
|
super.setOffSet(3);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
super.setOffSet(3);
|
if ((int) v1.getValue() != 0) {
|
||||||
|
super.setOffSet(offSet);
|
||||||
|
} else {
|
||||||
|
super.setOffSet(3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ public class TestJVM {
|
|||||||
Klass mainMeteKlass = bootClassLoader.loadClass(mainClass.getName().replace('.', '/'));
|
Klass mainMeteKlass = bootClassLoader.loadClass(mainClass.getName().replace('.', '/'));
|
||||||
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
|
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
|
||||||
Metaspace.registerJavaClass(mainMeteKlass);
|
Metaspace.registerJavaClass(mainMeteKlass);
|
||||||
|
|
||||||
JavaExecutionEngine.callMainMethod(mainKlassMethod);
|
JavaExecutionEngine.callMainMethod(mainKlassMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +147,10 @@ public class TestJVM {
|
|||||||
runMainClass(demo_finally_3.class);
|
runMainClass(demo_finally_3.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void demo_enum_1() throws Exception {
|
||||||
|
runMainClass(demo_enum_1.class); // TODO support enum
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_NEW() throws Exception {
|
public void test_NEW() throws Exception {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package haidnor.jvm.test.clazz;
|
package haidnor.jvm.test.clazz;
|
||||||
|
|
||||||
public class Human implements Organism {
|
public abstract class Human implements Organism {
|
||||||
|
|
||||||
public static final String HUMAN_NAME = "123";
|
public static final String HUMAN_NAME = "123";
|
||||||
|
|
||||||
|
|||||||
23
src/test/java/haidnor/jvm/test/clazz/StudentEnum.java
Normal file
23
src/test/java/haidnor/jvm/test/clazz/StudentEnum.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package haidnor.jvm.test.clazz;
|
||||||
|
|
||||||
|
public enum StudentEnum {
|
||||||
|
|
||||||
|
ZHANG_SAN(1,"张三");
|
||||||
|
|
||||||
|
private int age;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
StudentEnum(int age, String name) {
|
||||||
|
this.age = age;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAge() {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import haidnor.jvm.test.clazz.Student;
|
|||||||
public class Demo8 {
|
public class Demo8 {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Human organism1 = new Human();
|
Human organism1 = new Student();
|
||||||
Organism0 organism = new Student();
|
Organism0 organism = new Student();
|
||||||
organism.die();
|
organism.die();
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/test/java/haidnor/jvm/test/demo/demo_enum_1.java
Normal file
11
src/test/java/haidnor/jvm/test/demo/demo_enum_1.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package haidnor.jvm.test.demo;
|
||||||
|
|
||||||
|
import haidnor.jvm.test.clazz.StudentEnum;
|
||||||
|
|
||||||
|
public class demo_enum_1 {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
StudentEnum studentEnum = StudentEnum.ZHANG_SAN;
|
||||||
|
System.out.println(studentEnum.getAge());
|
||||||
|
System.out.println(studentEnum.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package haidnor.jvm.test.demo;
|
package haidnor.jvm.test.demo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 需要添加启动参数
|
* 需要添加启动参数
|
||||||
@@ -9,9 +10,13 @@ import java.util.ArrayList;
|
|||||||
public class demo_foreach_2 {
|
public class demo_foreach_2 {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ArrayList<Integer> list = new ArrayList<>();
|
List<Integer> list = new ArrayList<>();
|
||||||
list.add(1);
|
list.add(1);
|
||||||
list.add(2);
|
list.add(2);
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
list.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
for (Integer integer : list) {
|
for (Integer integer : list) {
|
||||||
System.out.println(integer);
|
System.out.println(integer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ public class demo_foreach_3 {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
List<Integer> list = List.of(1, 2, 3, 4, 5);
|
List<Integer> list = List.of(1, 2, 3, 4, 5);
|
||||||
for (Integer integer : list) {
|
list.add(6);
|
||||||
System.out.println(integer);
|
|
||||||
}
|
// for (Integer integer : list) {
|
||||||
|
// System.out.println(integer);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user