From f939eadff2423d7d42bffacb836d9307f83b3a98 Mon Sep 17 00:00:00 2001 From: wangxiang <276644985@qq.com> Date: Sat, 22 Jul 2023 16:06:24 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E6=94=AF=E6=8C=81=E8=BF=90=E8=A1=8C=20ja?= =?UTF-8?q?r=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- src/main/java/haidnor/jvm/Main.java | 33 ++++++++-- .../haidnor/jvm/classloader/ClassLoader.java | 36 +++++++++-- src/test/java/haidnor/jvm/test/TestJVM.java | 60 ++++++++++++++++--- 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index fe89c3f..3224ae5 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/main/java/haidnor/jvm/Main.java b/src/main/java/haidnor/jvm/Main.java index 564250c..197ee08 100644 --- a/src/main/java/haidnor/jvm/Main.java +++ b/src/main/java/haidnor/jvm/Main.java @@ -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 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); } } diff --git a/src/main/java/haidnor/jvm/classloader/ClassLoader.java b/src/main/java/haidnor/jvm/classloader/ClassLoader.java index 23616ed..46cc832 100644 --- a/src/main/java/haidnor/jvm/classloader/ClassLoader.java +++ b/src/main/java/haidnor/jvm/classloader/ClassLoader.java @@ -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 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(); diff --git a/src/test/java/haidnor/jvm/test/TestJVM.java b/src/test/java/haidnor/jvm/test/TestJVM.java index 3e4c81d..c1256a7 100644 --- a/src/test/java/haidnor/jvm/test/TestJVM.java +++ b/src/test/java/haidnor/jvm/test/TestJVM.java @@ -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 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; + } + } + } + } + } + }