add 支持运行 jar 文件

This commit is contained in:
wangxiang
2023-07-22 16:06:24 +08:00
parent 80f3de533e
commit f939eadff2
4 changed files with 115 additions and 20 deletions

View File

@@ -74,7 +74,7 @@ public void test_LSUB() throws Exception {
值得注意的是,这种方式编译运行的字节码文件是基于 java17 版本的。
## 运行 .class 文件
1. 使用 maven 命令将项目编译打包,得到 `haidnorJVM-1.0.jar` 文件
1. 使用 maven 命令将 haidnorJVM 编译打包,得到 `haidnorJVM-1.0.jar` 文件
2. 编写一个简单的程序,例如以下代码
```java
public class HelloWorld {
@@ -87,7 +87,9 @@ public void test_LSUB() throws Exception {
4. 使用 haidnorJVM 运行程序。执行命令 `java -jar haidnorJVM-1.0-SNAPSHOT.jar -class R:\HelloWorld.class`。注意需要 class 文件的绝对路径
## 运行 .jar 文件
开发中...
1. 使用 maven 命令将 haidnorJVM 编译打包,得到 `haidnorJVM-1.0.jar` 文件
2. 编写一个 java 项目编译打包成 .jar 文件,例如 demo.jar。要求 .jar 文件中的 META-INF/MANIFEST.MF 文件内有 `Main-Class` 属性 (含有 public static void main(String[] args) 方法的主类信息)
3. 使用 haidnorJVM 运行程序。执行命令 `java -jar haidnorJVM-1.0-SNAPSHOT.jar -class R:\demo.jar`。注意需要 jar 文件的绝对路径
# 完成度,已实现的 JVM 指令
```java

View File

@@ -17,6 +17,10 @@ import haidnor.jvm.util.JavaClassUtil;
import haidnor.jvm.util.JvmThreadHolder;
import lombok.SneakyThrows;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author wang xiang
*/
@@ -37,17 +41,36 @@ public class Main {
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("jar")) {
String path = cmd.getOptionValue("jar");
// TODO
}
if (cmd.hasOption("class")) {
String jarFilePath = cmd.getOptionValue("jar");
try (JarFile jarFile = new JarFile(jarFilePath)) {
ClassLoader bootClassLoader = new ClassLoader(jarFile, "ApplicationClassLoader");
String mainClass = jarFile.getManifest().getMainAttributes().getValue("Main-Class");
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
String className = entry.getName().replace('/', '.').substring(0, entry.getName().length() - 6);
if (className.equals(mainClass)) {
JvmThreadHolder.set(new JvmThread());
Klass mainMeteKlass = bootClassLoader.loadClass(jarFile, entry);
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
Metaspace.registerJavaClass(mainMeteKlass);
JavaExecutionEngine.callMainStaticMethod(mainKlassMethod);
break;
}
}
}
}
} else if (cmd.hasOption("class")) {
JvmThreadHolder.set(new JvmThread());
String path = cmd.getOptionValue("class");
ClassLoader bootClassLoader = new ClassLoader("ApplicationClassLoader");
Klass mainMeteKlass = bootClassLoader.loadClassWithAbsolutePath(path);
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
Metaspace.registerJavaClass(mainMeteKlass);
JavaExecutionEngine.callMainStaticMethod(mainKlassMethod);
}
}

View File

@@ -4,12 +4,12 @@ import haidnor.jvm.rtda.heap.Klass;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.*;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author wang xiang
@@ -20,6 +20,8 @@ public class ClassLoader {
public final static String rtJarPath;
public JarFile jarFile = null;
static {
URL resource = ClassLoader.class.getResource("/");
String path = resource.getPath();
@@ -42,16 +44,33 @@ public class ClassLoader {
this.name = name;
}
public ClassLoader(JarFile jarFile, String name) {
this.name = name;
this.jarFile = jarFile;
}
/**
* @param classPath 类路径,例如 haidnor/jvm/classloader/ClassLoader
*/
public Klass loadClass(String classPath) throws IOException {
ClassParser classParser;
ClassParser classParser = null;
if (classPath.startsWith("java/")) {
if (!new File(rtJarPath).exists()) {
throw new IllegalStateException("rt.jar not found");
}
classParser = new ClassParser(rtJarPath, classPath + ".class");
} else if (jarFile != null) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
String className = entry.getName().substring(0, entry.getName().length() - 6);
if (className.equals(classPath)) {
InputStream inputStream = jarFile.getInputStream(entry);
classParser = new ClassParser(inputStream, null);
}
}
}
} else {
URL resource = this.getClass().getResource("/");
String fileName = resource.getPath() + classPath + ".class";
@@ -62,6 +81,13 @@ public class ClassLoader {
return new Klass(this, javaClass);
}
public Klass loadClass(JarFile jarFile, JarEntry entry) throws IOException {
InputStream inputStream = jarFile.getInputStream(entry);
ClassParser classParser = new ClassParser(inputStream, null);
JavaClass javaClass = classParser.parse();
return new Klass(this, javaClass);
}
public Klass loadClassWithAbsolutePath(String absolutePath) throws IOException {
ClassParser classParser = new ClassParser(absolutePath);
JavaClass javaClass = classParser.parse();

View File

@@ -1,19 +1,12 @@
package haidnor.jvm.test;
import org.junit.Test;
import haidnor.jvm.classloader.ClassLoader;
import haidnor.jvm.core.JavaExecutionEngine;
import haidnor.jvm.rtda.heap.Klass;
import haidnor.jvm.rtda.heap.KlassMethod;
import haidnor.jvm.rtda.metaspace.Metaspace;
import haidnor.jvm.runtime.JvmThread;
import haidnor.jvm.test.demo.Demo1;
import haidnor.jvm.test.demo.Demo2;
import haidnor.jvm.test.demo.Demo3;
import haidnor.jvm.test.demo.Demo4;
import haidnor.jvm.test.demo.Demo5;
import haidnor.jvm.test.demo.Demo6;
import haidnor.jvm.test.demo.*;
import haidnor.jvm.test.instruction.Array;
import haidnor.jvm.test.instruction.DO_WHILE;
import haidnor.jvm.test.instruction.math.ISUB;
@@ -23,6 +16,13 @@ import haidnor.jvm.test.instruction.references.NEW;
import haidnor.jvm.util.JavaClassUtil;
import haidnor.jvm.util.JvmThreadHolder;
import lombok.SneakyThrows;
import org.junit.Test;
import java.io.IOException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class TestJVM {
@@ -99,4 +99,48 @@ public class TestJVM {
public void test_INVOKEINTERFACE() throws Exception {
runMainClass(INVOKEINTERFACE.class);
}
@Test
public void test_() throws Exception {
String jarFilePath = "D:\\project_java\\JvmDemo\\target\\JvmDemo-1.0-SNAPSHOT.jar";
try (JarFile jarFile = new JarFile(jarFilePath)) {
Manifest manifest = jarFile.getManifest();
// 读取指定键的值
String mainClass = manifest.getMainAttributes().getValue("Main-Class");
System.out.println(mainClass);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test_jar() throws Exception {
String jarFilePath = "D:/project_java/JvmDemo/target/JvmDemo-1.0-SNAPSHOT.jar";
JvmThreadHolder.set(new JvmThread());
try (JarFile jarFile = new JarFile(jarFilePath)) {
ClassLoader bootClassLoader = new ClassLoader(jarFile, "ApplicationClassLoader");
// 找到主类 a.b.Main
String mainClass = jarFile.getManifest().getMainAttributes().getValue("Main-Class");
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
String className = entry.getName().replace('/', '.').substring(0, entry.getName().length() - 6);
if (className.equals(mainClass)) {
Klass mainMeteKlass = bootClassLoader.loadClass(jarFile, entry);
KlassMethod mainKlassMethod = JavaClassUtil.getMainMethod(mainMeteKlass);
Metaspace.registerJavaClass(mainMeteKlass);
JavaExecutionEngine.callMainStaticMethod(mainKlassMethod);
break;
}
}
}
}
}
}