Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

修正:pc处理逻辑;新增:跳转指令(if_cmple, goto)+iload_n指令 #9

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
112 changes: 97 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,113 @@

这是一个Java实现的JAVA虚拟机,它会非常简单,实际上简单的只够运行HelloWorld。虽然简单,但尽量符合 JVM 标准,目前主要参考依据是[《Java虚拟机规范 (Java SE 7 中文版)》](http://www.iteye.com/topic/1117824)。

关于此项目的说明,[详见此文](http://www.jianshu.com/p/4d81465c2fb8)。

## 运行
非原创,原项目来自 https://github.com/caoym/jjvm

先写一个HelloWorld,代码如下:
+ 运行方式有改动 (原项目运行方式在我的环境不work)
+ pc处理机制有修改

原实现无法支持条件跳转

```java
package org.caoym;
+ opcode 新增指令

public class HelloWorld {
public static void main(String[] args){
System.out.println("Hello World");
}
}
增加if_cmple,goto
增加iload_n


## 参考项目
+ java版jvm实现( https://github.com/zachaxy/JVM )
+ go版jvm实现( https://github.com/zxh0/jvmgo-book )


# 用法

## 环境
SUN jdk1.8 (OpenJdk1.8 运行缺少一些jar,用SUN JDK重装后解决)

```shell
$java -version
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
```

可以通过以下命令运行:
### 设置java环境
将以下内容加入 /etc/profile ,保存后执行 source /etc/profile 使之生效

```shell
$ java /home/myclasspath org.caoym.jjvm.JJvm org.caoym.HelloWorld
Hello World
JAVA_HOME=/usr/java/jdk1.8.0_211-amd64
JRE_HOME=$JAVA_HOME/jre
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export JAVA_HOME JRE_HOME PATH CLASSPATH
```

## 可运行的示例
## 编译和运行
可以通过以下编译命令运行:
### Sample1
基本HelloWorld

[用于测试 JJvm 的示例代码可在此处下载](https://github.com/caoym/jjvm-samples)
```shell
#编译 sample1
$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true
$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked
$javac org/caoym/samples/sample1/HelloWorld.java

#运行
$java org.caoym.jjvm.JJvm . org.caoym.samples.sample1.HelloWorld
```

### Sample2
带有Interface、父子类继承的Helloworld

```shell
#编译 sample2
$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true
$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked
$javac org/caoym/samples/sample2/Main.java

#运行(不加运行参数如inputVar1,会报错)
$java org.caoym.jjvm.JJvm . org.caoym.samples.sample2.Main inputVar1
```

### Sample3
带有条件跳转的Helloworld

```shell
#编译 sample3
$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true
$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked
$javac org/caoym/samples/sample3/HelloWorld.java

#运行
$java org.caoym.jjvm.JJvm . org.caoym.samples.sample3.HelloWorld
```

### Sample4
线程

```shell
#编译 sample4
$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true
$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked
$javac org/caoym/samples/sample4/TestThread.java

#运行
$java org.caoym.jjvm.JJvm . org.caoym.samples.sample4.TestThread
```

## FAQ
### error: package jdk.internal.org.objectweb.asm does not exist
```shell
默认 javac 不读取 rt.jar 中 classes内容,只读取符号表
符号表一般只包括标准API和部分其他API,如 com.sun., com.oracle. and sun.*

编译选项加上 -XDignore.symbol.file=true
javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true
```

### 未能自动清理旧class文件,重新编译前手动清理一下,执行
```shell
$find . -name *.class -type f -print -exec rm -rf {} \;
```
2 changes: 1 addition & 1 deletion src/main/java/org/caoym/jjvm/VirtualMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void run(String[] args) throws Exception {
"main",
"([Ljava/lang/String;)V");
//执行入口方法
method.call(env, null, new Object[]{args});
method.call(env, null, new Object[]{args});//TODO:多个class文件,是如何组织?何时加载的?
}

public JvmClass getClass(String className) throws ClassNotFoundException {
Expand Down
71 changes: 65 additions & 6 deletions src/main/java/org/caoym/jjvm/opcode/BytecodeInterpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ public static void run(Env env) throws Exception {
}
continue;
}
OpcodeInvoker[] codes = frame.getOpcodes();
int pc = frame.increasePC();
OpcodeInvoker[] ops = frame.getOps();
int pc = frame.getPC();

StringBuilder sb = new StringBuilder();
sb.append("> ");
sb.append(frame.getCurrentClass().getName());
Expand All @@ -44,12 +45,30 @@ public static void run(Env env) throws Exception {
sb.append("@");
sb.append(pc);
sb.append(":");
sb.append(codes[pc]);
sb.append(ops[pc]);
sb.append("[");
sb.append(ops[pc].getNoOfOperands());
sb.append("]");
System.out.println(sb);
codes[pc].invoke(env, frame);

//execute opcode
int ret = ops[pc].invoke(env, frame);

//move forward
if(ret == 0) {
frame.increasePC(1+ops[pc].getNoOfOperands());
}else {
frame.increasePC(pc + ret);
}
}
}

/**
* 返回内容以 opcode 为主
*
* @param codes
* @return
*/
public static OpcodeInvoker[] parseCodes(byte[] codes){
ArrayList<OpcodeInvoker> opcodes = new ArrayList<>();
for(int i=0; i<codes.length; i++){
Expand All @@ -59,17 +78,57 @@ public static OpcodeInvoker[] parseCodes(byte[] codes){
byte[] operands = Arrays.copyOfRange(codes, i + 1, i + 1 + noOfOperands);
opcodes.add(new OpcodeInvoker() {
@Override
public void invoke(Env env, StackFrame frame) throws Exception {
public int invoke(Env env, StackFrame frame) throws Exception {
route.invoke(env, frame, operands);
return 0;
}
@Override
public String toString() {
return route.name();
}
@Override
public int getNoOfOperands() {
// TODO Auto-generated method stub
return 0;
}
});
i += noOfOperands;
}
return Arrays.copyOf(opcodes.toArray(), opcodes.size(), OpcodeInvoker[].class);
}


/**
* 返回内容以 opcode 和 operands 为主
* @param codes
* @return
*/
public static OpcodeInvoker[] parseOps(byte[] codes){
ArrayList<OpcodeInvoker> ops = new ArrayList<>();
for(int i=0; i<codes.length; i++){
short opcode = (short)(0xff&codes[i]);
final OpcodeRout route = OpcodeRout.valueOf(opcode);
short noOfOperands = Constants.NO_OF_OPERANDS[opcode];
byte[] operands = Arrays.copyOfRange(codes, i + 1, i + 1 + noOfOperands);
ops.add(new OpcodeInvoker() {
@Override
public int invoke(Env env, StackFrame frame) throws Exception {
route.invoke(env, frame, operands);
return 0;
}
@Override
public String toString() {
return route.name();
}
public int getNoOfOperands() {
return noOfOperands;
}
});

for(int si = 0; si < noOfOperands; si++) {
ops.add(null);
}
i += noOfOperands;
}
return Arrays.copyOf(ops.toArray(), ops.size(), OpcodeInvoker[].class);
}
}
4 changes: 4 additions & 0 deletions src/main/java/org/caoym/jjvm/opcode/JvmOpcodeMethod.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.caoym.jjvm.opcode;


import com.sun.tools.classfile.*;
import org.caoym.jjvm.lang.JvmMethod;
import org.caoym.jjvm.runtime.*;
Expand All @@ -12,6 +13,7 @@ public class JvmOpcodeMethod implements JvmMethod {
private final JvmOpcodeClass clazz;
private final Method method;
private final OpcodeInvoker[] opcodes;
private final OpcodeInvoker[] ops;
private final Code_attribute codeAttribute;
private final String name;
private final int parameterCount;
Expand All @@ -21,6 +23,7 @@ public JvmOpcodeMethod(JvmOpcodeClass clazz, Method method) throws ConstantPoolE
this.clazz = clazz;
this.method = method;
codeAttribute = (Code_attribute)method.attributes.get("Code");
ops = BytecodeInterpreter.parseOps(codeAttribute.code);
opcodes = BytecodeInterpreter.parseCodes(codeAttribute.code);
String temp = "";
temp = method.getName(clazz.getClassFile().constant_pool);
Expand All @@ -40,6 +43,7 @@ public void call(Env env, Object thiz, Object ...args) throws Exception {
this,
clazz.getClassFile().constant_pool,
opcodes,
ops,
codeAttribute.max_locals,
codeAttribute.max_stack);

Expand Down
5 changes: 4 additions & 1 deletion src/main/java/org/caoym/jjvm/opcode/OpcodeInvoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
import org.caoym.jjvm.runtime.StackFrame;

public interface OpcodeInvoker {
public void invoke(Env env, StackFrame frame) throws Exception ;

public int invoke(Env env, StackFrame frame) throws Exception ;

public int getNoOfOperands();
}
Loading