写在前面
本文看下如何模拟数组相关的操作,主要是实现数组相关的指令,关于数组相关的指令可以参考这篇文章。
1:正文
简单起见这里我们仅仅实现int基础数据类型的一维数组。
- newarray指令对应的类
package com.dahuyou.tryy.too.simulate.interpreter.instructions.references;
import com.dahuyou.tryy.too.simulate.interpreter.instructions.base.BytecodeReader;
import com.dahuyou.tryy.too.simulate.interpreter.instructions.base.Instruction;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.Frame;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.OperandStack;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.ClassLoader;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Class;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Object;
public class NEW_ARRAY implements Instruction {
private byte atype;
@Override
public void fetchOperands(BytecodeReader reader) {
this.atype = reader.readByte();
}
@Override
public void execute(Frame frame) {
// 获取操作数栈
OperandStack stack = frame.operandStack();
// 弹出操作数栈栈顶的int类型整数,作为将要创建的数组的大小
int count = stack.popInt();
if (count < 0) {
throw new NegativeArraySizeException();
}
// 获取类加载器
ClassLoader classLoader = frame.method().clazz().loader();
Class arrClass = getPrimitiveArrayClass(classLoader, this.atype);
Object arr = arrClass.newArray(count);
stack.pushRef(arr);
}
// ...
}
- iastore指令对应的类
/**
* 将栈顶的int类型数据存储到指定数组的指定位置
* 即给int数组某位置元素赋值😂😂😂
*/
public class IASTORE extends InstructionNoOperands {
@Override
public void execute(Frame frame) {
OperandStack stack = frame.operandStack();
// 操作数栈中弹出一个元素作为要设置的目标值
int val = stack.popInt();
// 操作数栈中弹出一个元素作为要设置的元素的位置
int idx = stack.popInt();
System.out.println("IASTORE指令:从操作数栈栈中弹出要设置的元素和位置");
Object arrRef = stack.popRef();
checkNotNull(arrRef);
// 这里是通过将真正的数组数据存储到Object中来模拟的,所以还是从Object中来获取,注意这里的Object是我们自定义的模拟的Object,不是jdk的java.lang.Object
int[] ints = arrRef.ints();
checkIndex(ints.length, idx);
// 完成数组赋值,IASTORE指令工作done!
ints[idx] = val;
System.out.println("IASTORE指令:完成数组元素赋值");
}
}
- iaload对应的类
public class IALOAD extends InstructionNoOperands {
@Override
public void execute(Frame frame) {
OperandStack stack = frame.operandStack();
// 从操作数栈中弹出要获取元素的数组索引位置
int idx = stack.popInt();
Object arrRef = stack.popRef();
checkNotNull(arrRef);
int[] ints = arrRef.ints();
checkIndex(ints.length, idx);
// 从指定的索引位置获取数组元素,并将获取到的元素要到操作数栈的栈顶
stack.pushInt(ints[idx]);
System.out.println("IALOAD指令:从操作数栈中弹出要获取元素的数组索引位置");
System.out.println("IALOAD指令:从指定的索引位置获取数组元素,并将获取到的元素要到操作数栈的栈顶");
}
}
- arraylength指令
public class ARRAY_LENGTH extends InstructionNoOperands {
@Override
public void execute(Frame frame) {
OperandStack stack = frame.operandStack();
Object arrRef = stack.popRef();
if (null == arrRef){
throw new NullPointerException();
}
int arrLen = arrRef.arrayLength();
stack.pushInt(arrLen);
System.out.println("ARRAY_LENGTH: 指令执行,获取数组长度并将结果压倒操作数栈的栈顶");
}
}
- 测试
main测试类:
package com.dahuyou.tryy.too.simulate.interpreter;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.classpath.Classpath;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile.ClassFile;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile.MemberInfo;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.cmd.Cmd;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.ClassLoader;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Class;
import com.dahuyou.tryy.too.simulate.interpreter.runtime.area.heap.methodarea.Method;
/**
* -Xthejrepath D:\programs\javas\java1.8/jre -Xthetargetclazz D:\test\itstack-demo-jvm-master\tryy-too-simulate-classload-load-clazz\target\test-classes\org\itstack\demo\test\HelloWorld
*/
public class Main {
public static void main(String[] args) {
Cmd cmd = Cmd.parse(args);
if (!cmd.ok || cmd.helpFlag) {
System.out.println("Usage: <main class> [-options] class [args...]");
return;
}
if (cmd.versionFlag) {
//注意案例测试都是基于1.8,另外jdk1.9以后使用模块化没有rt.jar
System.out.println("java version \"1.8.0\"");
return;
}
startJVM(cmd);
}
private static void startJVM(Cmd cmd) {
// 创建classpath
Classpath cp = new Classpath(cmd.thejrepath, cmd.classpath);
// System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs());
System.out.printf("classpath:%s parsed class:%s \n", cp, cmd.thetargetclazz);
//获取className
// String className = cmd.getMainClass().replace(".", "/");
try {
// byte[] classData = cp.readClass(className);
/*byte[] classData = cp.readClass(cmd.thetargetclazz.replace(".", "/"));
System.out.println(Arrays.toString(classData));
System.out.println("classData:");
for (byte b : classData) {
//16进制输出
System.out.print(String.format("%02x", b & 0xff) + " ");
}*/
// 创建类加载器准备加载类
/**
* 加载3个阶段
* 1:加载
* 找到字节码,并将其存储到原元空间(<=7方法区),然后该类,该类父类,父接口也加载并在堆中生成对应的Class对象
* 2:链接
* 验证:验证文件内容的合法性,如是否cafebabe打头,结构是否符合定义
* 准备:主要是给静态变量申请内存空间,以及赋初始值,如int,short这种则给默认值0
* 解析:符号引用(指向类或者方法的一个字符串)转换为直接引用(jvm的内存地址)
* 3:初始化
* 执行<init>,<clinit>方法,完成静态变量的赋值
*/
ClassLoader classLoader = new ClassLoader(cp);
String clazzName = cmd.thetargetclazz.replace(".", "/");
Class mainClass = classLoader.loadClass(clazzName);
Method mainMethod = mainClass.getMainMethod();
new Interpreter(mainMethod, true);
/*// 创建className对应的ClassFile对象
ClassFile classFile = loadClass(clazzName, cp);
MemberInfo mainMethod = getMainMethod(classFile);
if (null == mainMethod) {
System.out.println("Main method not found in class " + cmd.classpath);
return;
}
// 核心重点代码:通过解释器来执行main方法
new Interpreter(mainMethod);*/
} catch (Exception e) {
System.out.println("Could not find or load main class " + cmd.getMainClass());
e.printStackTrace();
}
}
/**
* 获取main函数,这里我们要模拟是执行器执行main函数的过程,当然其他方法也是一样的!!!
* @param classFile
* @return
*/
private static MemberInfo getMainMethod(ClassFile classFile) {
if (null == classFile) return null;
MemberInfo[] methods = classFile.methods();
for (MemberInfo m : methods) {
if ("main".equals(m.name()) && "([Ljava/lang/String;)V".equals(m.descriptor())) {
return m;
}
}
return null;
}
/**
* 生成class文件对象
* @param clazzName
* @param cp
* @return
*/
private static ClassFile loadClass(String clazzName, Classpath cp) {
try {
// 获取类class对应的byte数组
byte[] classData = cp.readClass(clazzName);
return new ClassFile(classData);
} catch (Exception e) {
System.out.println("无法加载到类: " + clazzName);
return null;
}
}
}
定义需要解析的类:
public class HelloWorld {
public static void main(String[] args) {
/* long x = fibonacci(10);
System.out.println(x);*/
// returnALong();
// new int[8]; newarray指令
int[] intArr = new int[8];
// iastore指令
intArr[0] = 89;
// iaload指令
int pos0 = intArr[0];
// arraylength指令
int arraylength = intArr.length;
}
public static long returnALong() {
long longResult = 99;
return longResult;
}
//斐波那契数列(Fibonacci sequence)
/*private static long fibonacci(long n) {
if (n <= 1) {
return n;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}*/
}
配置program argument:
运行:
写在后面
参考文章列表
Java数组的类名是什么以及数组相关操作的指令有什么? 。