案例介绍
本章主要介绍用java实现一些本地方法类库,并初始化本地方法,之后通过反射命令来调用本地方法。
Java虚拟机和Java类库一起构成了Java运行时环境。Java类库主要用Java语言编写,一些无法用Java语言实现的方法则使用本地语言编写,这额方法叫作本地方法。 OpenJDK类库中的本地方法是用JNI(Java Native Interface)编写的,但是要让虚拟机支持JNI规范还需要大量工作。
环境准备
jdk 1.8.0
IntelliJ IDEA Community Edition 2018.3.1 x64
配置信息
调试配置
配置位置:Run/Debug Configurations -> program arguments
配置内容:-Xjre “C:\Program Files\Java\jdk1.8.0_161\jre” E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-09\target\test-classes\org\itstack\demo\test\HelloWorld
代码示例
itstack-demo-jvm-09
├── pom.xml
└── src
└── main
│ └── java
│ └── org.itstack.demo.jvm
│ ├── _native
│ │ ├── java
│ │ │ ├── _Class.java
│ │ │ ├── _Double.java
│ │ │ ├── _Float.java
│ │ │ ├── _Object.java
│ │ │ ├── _String.java
│ │ │ └── _System.java
│ │ └── sun
│ ├── NativeMethod.java
│ └── Registry.java
│ ├── classfile
│ │ ├── attributes
│ │ ├── constantpool
│ │ ├── ClassFile.java
│ │ ├── ClassReader.java
│ │ └── MemberInfo.java
│ ├── classpath
│ │ ├── impl
│ │ │ ├── CompositeEntry.java
│ │ │ ├── DirEntry.java
│ │ │ ├── WildcardEntry.java
│ │ │ └── ZipEntry.java
│ │ ├── Classpath.java
│ │ └── Entry.java
│ ├── classpath
│ │ ├── base
│ │ │ ├── BytecodeReader.java
│ │ │ ├── ClassInitLogic.java
│ │ │ ├── Instruction.java
│ │ │ ├── InstructionBranch.java
│ │ │ ├── InstructionIndex8.java
│ │ │ ├── InstructionIndex16.java
│ │ │ ├── InstructionNoOperands.java
│ │ │ └── MethodInvokeLogic.java
│ │ ├── comparisons
│ │ ├── constants
│ │ ├── control
│ │ ├── conversions
│ │ ├── extended
│ │ ├── loads
│ │ ├── math
│ │ ├── references
│ │ │ ├── ANEW_ARRAY.java
│ │ │ ├── ARRAY_LENGTH.java
│ │ │ ├── CHECK_CAST.java
│ │ │ ├── GET_FIELD.java
│ │ │ ├── GET_STATIC.java
│ │ │ ├── INSTANCE_OF.java
│ │ │ ├── INVOKE_INTERFACE.java
│ │ │ ├── INVOKE_SPECIAL.java
│ │ │ ├── INVOKE_STATIC.java
│ │ │ ├── INVOKE_VIRTUAL.java
│ │ │ ├── MULTI_ANEW_ARRAY.java
│ │ │ ├── NEW.java
│ │ │ ├── NEW_ARRAY.java
│ │ │ ├── PUT_FIELD.java
│ │ │ └── PUT_STATIC.java
│ │ ├── reserved
│ │ │ └── INVOKE_NATIVE.java
│ │ ├── stack
│ │ ├── store
│ │ │ └── xastore
│ │ │ ├── AASTORE.java
│ │ │ ├── BASTORE.java
│ │ │ ├── CASTORE.java
│ │ │ ├── DASTORE.java
│ │ │ ├── FASTORE.java
│ │ │ ├── IASTORE.java
│ │ │ ├── LASTORE.java
│ │ │ └── SASTORE.java
│ │ └── Factory
│ ├── rtda
│ │ ├── heap
│ │ │ ├── constantpool
│ │ │ ├── methodarea
│ │ │ │ ├── Class.java
│ │ │ │ ├── ClassMember.java
│ │ │ │ ├── Field.java
│ │ │ │ ├── Method.java
│ │ │ │ ├── MethodDescriptor.java
│ │ │ │ ├── MethodDescriptorParser.java
│ │ │ │ ├── MethodLookup.java
│ │ │ │ ├── Object.java
│ │ │ │ ├── Slots.java
│ │ │ │ └── StringPool.java
│ │ │ └── ClassLoader.java
│ │ ├── Frame.java
│ │ ├── JvmStack.java
│ │ ├── LocalVars.java
│ │ ├── OperandStack.java
│ │ ├── Slot.java
│ │ └── Thread.java
│ ├── Cmd.java
│ ├── Interpret.java
│ └── Main.java
└── test
└── java
└── org.itstack.demo.test
└── HelloWorld.java
代码片段
_Class.java
package org.itstack.demo.jvm._native.java;
import org.itstack.demo.jvm._native.NativeMethod;
import org.itstack.demo.jvm._native.Registry;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.LocalVars;
import org.itstack.demo.jvm.rtda.OperandStack;
import org.itstack.demo.jvm.rtda.heap.ClassLoader;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;
import org.itstack.demo.jvm.rtda.heap.methodarea.StringPool;
/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/30
*/
public class _Class {
private final String jlClass = "java/lang/Class";
public _Class() {
Registry.register(jlClass, "getPrimitiveClass", "(Ljava/lang/String;)Ljava/lang/Class;", new NativeMethod(this, "getPrimitiveClass"));
Registry.register(jlClass, "getName0", "()Ljava/lang/String;", new NativeMethod(this, "getName0"));
Registry.register(jlClass, "desiredAssertionStatus0", "(Ljava/lang/Class;)Z", new NativeMethod(this, "desiredAssertionStatus0"));
Registry.register(jlClass, "registerNatives", "()V", new NativeMethod(this, "registerNatives"));
}
public void registerNatives(Frame frame) {
// do nothing
}
public void getPrimitiveClass(Frame frame) {
Object nameObj = frame.localVars().getRef(0);
String name = StringPool.goString(nameObj);
ClassLoader loader = frame.method().clazz().loader();
Object jClass = loader.loadClass(name).jClass();
frame.operandStack().pushRef(jClass);
}
public void getName0(Frame frame) {
Object thiz = frame.localVars().getThis();
Class clazz = (Class) thiz.extra();
String name = "虚拟机本地方法getName0获取类名:" + clazz.javaName();
Object nameObj = StringPool.jString(clazz.loader(), name);
frame.operandStack().pushRef(nameObj);
}
public void desiredAssertionStatus0(Frame frame) {
frame.operandStack().pushBoolean(false);
}
public void isInterface(Frame frame) {
LocalVars vars = frame.localVars();
Object thiz = vars.getThis();
Class clazz = (Class) thiz.extra();
OperandStack stack = frame.operandStack();
stack.pushBoolean(clazz.isInterface());
}
public void isPrimitive(Frame frame) {
LocalVars vars = frame.localVars();
Object thiz = vars.getThis();
Class clazz = (Class) thiz.extra();
OperandStack stack = frame.operandStack();
stack.pushBoolean(clazz.IsPrimitive());
}
}
_System.java
package org.itstack.demo.jvm._native.java;
import org.itstack.demo.jvm._native.NativeMethod;
import org.itstack.demo.jvm._native.Registry;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.LocalVars;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;
/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/30
*/
public class _System {
private final String jlSystem = "java/lang/System";
public _System() {
Registry.register(jlSystem, "arraycopy", "()Ljava/lang/String;", new NativeMethod(this, "arraycopy"));
Registry.register(jlSystem,"registerNatives", "()V",new NativeMethod(this,"registerNatives"));
}
public void registerNatives(Frame frame) {
// do nothing
}
public void arraycopy(Frame frame) {
LocalVars vars = frame.localVars();
Object src = vars.getRef(0);
int srcPos = vars.getInt(1);
Object dest = vars.getRef(2);
int destPos = vars.getInt(4);
int length = vars.getInt(4);
if (null == src || dest == null) {
throw new NullPointerException();
}
if (!checkArrayCopy(src, dest)) {
throw new ArrayStoreException();
}
if (srcPos < 0 || destPos < 0 || length < 0 ||
srcPos + length > src.arrayLength() ||
destPos + length > dest.arrayLength()) {
throw new IndexOutOfBoundsException();
}
System.arraycopy(src, srcPos, dest, destPos, length);
//todo 待完善
}
public boolean checkArrayCopy(Object src, Object dest) {
Class srcClass = src.clazz();
Class destClass = dest.clazz();
if (!srcClass.isArray() || !destClass.isArray()) {
return false;
}
if (srcClass.componentClass().IsPrimitive() || destClass.componentClass().IsPrimitive()) {
return srcClass == destClass;
}
return true;
}
}
NativeMethod.java
package org.itstack.demo.jvm._native;
import org.itstack.demo.jvm.rtda.Frame;
import java.lang.reflect.Method;
/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/30
*/
public class NativeMethod {
private String methodName;
private Object obj;
public NativeMethod(Object obj, String methodName) {
this.methodName = methodName;
this.obj = obj;
}
public void invoke(Frame frame) {
try {
Method method = obj.getClass().getMethod(methodName, frame.getClass());
method.invoke(obj, frame);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Registry.java
package org.itstack.demo.jvm._native;
import org.itstack.demo.jvm._native.java.*;
import java.util.HashMap;
import java.util.Map;
/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/30
*/
public class Registry {
private static Map registry = new HashMap<>();
//初始化本地方法
public static void initNative() {
new _Class();
new _Double();
new _Float();
new _Object();
new _String();
new _System();
}
public static void register(String className, String methodName, String methodDescriptor, NativeMethod method) {
String key = className + "~" + methodName + "~" + methodDescriptor;
registry.put(key, method);
}
public static NativeMethod findNativeMethod(String className, String methodName, String methodDescriptor) {
String key = className + "~" + methodName + "~" + methodDescriptor;
return registry.get(key);
}
}
INVOKE_NATIVE.java
package org.itstack.demo.jvm.instructions.reserved;
import org.itstack.demo.jvm._native.NativeMethod;
import org.itstack.demo.jvm._native.Registry;
import org.itstack.demo.jvm.instructions.base.InstructionNoOperands;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
/**
* http://www.itstack.org
* create by fuzhengwei on 2019/5/2
*/
public class INVOKE_NATIVE extends InstructionNoOperands {
@Override
public void execute(Frame frame) {
Method method = frame.method();
String className = method.clazz().name();
String methodName = method.name();
String methodDescriptor = method.descriptor();
NativeMethod nativeMethod = Registry.findNativeMethod(className, methodName, methodDescriptor);
if (null == nativeMethod) {
String methodInfo = className + "." + methodName + methodDescriptor;
throw new UnsatisfiedLinkError(methodInfo);
}
nativeMethod.invoke(frame);
}
}
ClassLoader.java
package org.itstack.demo.jvm.rtda.heap;
import org.itstack.demo.jvm.classfile.ClassFile;
import org.itstack.demo.jvm.classpath.Classpath;
import org.itstack.demo.jvm.rtda.heap.constantpool.AccessFlags;
import org.itstack.demo.jvm.rtda.heap.methodarea.*;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;
import java.util.HashMap;
import java.util.Map;
/*
class names:
- primitive types: boolean, byte, int ...
- primitive arrays: [Z, [B, [I ...
- non-array classes: java/lang/Object ...
- array classes: [Ljava/lang/Object; ...
*/
public class ClassLoader {
private Classpath classpath;
private Map classMap;
public ClassLoader(Classpath classpath) {
this.classpath = classpath;
this.classMap = new HashMap<>();
this.loadBasicClasses();
this.loadPrimitiveClasses();
}
private void loadBasicClasses() {
Class jlClassClass = this.loadClass("java/lang/Class");
for (Map.Entry entry : this.classMap.entrySet()) {
Class clazz = entry.getValue();
if (clazz.jClass == null) {
clazz.jClass = jlClassClass.newObject();
clazz.jClass.extra = clazz;
}
}
}
private void loadPrimitiveClasses() {
for (Map.Entry entry : ClassNameHelper.primitiveTypes.entrySet()) {
loadPrimitiveClass(entry.getKey());
}
}
private void loadPrimitiveClass(String className) {
Class clazz = new Class(AccessFlags.ACC_PUBLIC,
className,
this,
true);
clazz.jClass = this.classMap.get("java/lang/Class").newObject();
clazz.jClass.extra = clazz;
this.classMap.put(className, clazz);
}
public Class loadClass(String className) {
Class clazz = classMap.get(className);
if (null != clazz) return clazz;
//'['数组标识
if (className.getBytes()[0] == '[') {
clazz = loadArrayClass(className);
} else {
clazz = loadNonArrayClass(className);
}
Class jlClazz = this.classMap.get("java/lang/Class");
if (null != jlClazz && null != clazz) {
clazz.jClass = jlClazz.newObject();
clazz.jClass.extra = clazz;
}
return clazz;
}
private Class loadArrayClass(String className) {
Class clazz = new Class(AccessFlags.ACC_PUBLIC,
className,
this,
true,
this.loadClass("java/lang/Object"),
new Class[]{
this.loadClass("java/lang/Cloneable"),
this.loadClass("java/io/Serializable")});
this.classMap.put(className, clazz);
return clazz;
}
private Class loadNonArrayClass(String className) {
try {
byte[] data = this.classpath.readClass(className);
if (null == data) {
throw new ClassNotFoundException(className);
}
Class clazz = defineClass(data);
link(clazz);
return clazz;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void link(Class clazz) {
verify(clazz);
prepare(clazz);
}
private void prepare(Class clazz) {
calcInstanceFieldSlotIds(clazz);
calcStaticFieldSlotIds(clazz);
allocAndInitStaticVars(clazz);
}
private void allocAndInitStaticVars(Class clazz) {
clazz.staticVars = new Slots(clazz.staticSlotCount);
for (Field field : clazz.fields) {
if (field.isStatic() && field.isFinal()) {
initStaticFinalVar(clazz, field);
}
}
}
private void initStaticFinalVar(Class clazz, Field field) {
Slots staticVars = clazz.staticVars;
RunTimeConstantPool constantPool = clazz.runTimeConstantPool;
int cpIdx = field.constValueIndex();
int slotId = field.slotId();
if (cpIdx > 0) {
switch (field.descriptor()) {
case "Z":
case "B":
case "C":
case "S":
case "I":
java.lang.Object val = constantPool.getConstants(cpIdx);
staticVars.setInt(slotId, (Integer) val);
break;
case "J":
staticVars.setLong(slotId, (Long) constantPool.getConstants(cpIdx));
break;
case "F":
staticVars.setFloat(slotId, (Float) constantPool.getConstants(cpIdx));
break;
case "D":
staticVars.setDouble(slotId, (Double) constantPool.getConstants(cpIdx));
break;
case "Ljava/lang/String;":
String goStr = (String) constantPool.getConstants(cpIdx);
Object jStr = StringPool.jString(clazz.loader(), goStr);
staticVars.setRef(slotId, jStr);
break;
}
}
}
private void calcStaticFieldSlotIds(Class clazz) {
int slotId = 0;
for (Field field : clazz.fields) {
if (field.isStatic()) {
field.slotId = slotId;
slotId++;
if (field.isLongOrDouble()) {
slotId++;
}
}
}
clazz.staticSlotCount = slotId;
}
private void calcInstanceFieldSlotIds(Class clazz) {
int slotId = 0;
if (clazz.superClass != null) {
slotId = clazz.superClass.instanceSlotCount;
}
for (Field field : clazz.fields) {
if (!field.isStatic()) {
field.slotId = slotId;
slotId++;
if (field.isLongOrDouble()) {
slotId++;
}
}
}
clazz.instanceSlotCount = slotId;
}
private void verify(Class clazz) {
// 校验字节码,尚未实现
}
private Class defineClass(byte[] data) throws Exception {
Class clazz = parseClass(data);
clazz.loader = this;
resolveSuperClass(clazz);
resolveInterfaces(clazz);
this.classMap.put(clazz.name, clazz);
return clazz;
}
private void resolveInterfaces(Class clazz) throws Exception {
int interfaceCount = clazz.interfaceNames.length;
if (interfaceCount > 0) {
clazz.interfaces = new Class[interfaceCount];
for (int i = 0; i < interfaceCount; i++) {
clazz.interfaces[i] = clazz.loader.loadClass(clazz.interfaceNames[i]);
}
}
}
private void resolveSuperClass(Class clazz) throws Exception {
if (!clazz.name.equals("java/lang/Object")) {
clazz.superClass = clazz.loader.loadClass(clazz.superClassName);
}
}
private Class parseClass(byte[] data) {
ClassFile classFile = new ClassFile(data);
return new Class(classFile);
}
}
HelloWorld.java
package org.itstack.demo.test;
/**
* -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-09\target\test-classes\org\itstack\demo\test\HelloWorld -verbose true -args 你好,java版虚拟机v1.0,欢迎你的到来。
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println(byte.class.getName()); // byte
System.out.println(void.class.getName()); // void
System.out.println(boolean.class.getName()); // boolean
System.out.println(char.class.getName()); // char
System.out.println(short.class.getName()); // short
System.out.println(int.class.getName()); // int
System.out.println(long.class.getName()); // long
System.out.println(float.class.getName()); // float
System.out.println(double.class.getName()); // double
System.out.println(Object.class.getName()); // java.lang.Object
System.out.println(int[].class.getName()); // [I
System.out.println(int[][].class.getName()); // [[I
System.out.println(Object[].class.getName()); // [Ljava.lang.Object;
System.out.println(Object[][].class.getName()); // [[Ljava.lang.Object;
}
}
测试结果
虚拟机本地方法getName0获取类名:byte
虚拟机本地方法getName0获取类名:void
虚拟机本地方法getName0获取类名:boolean
虚拟机本地方法getName0获取类名:char
虚拟机本地方法getName0获取类名:short
虚拟机本地方法getName0获取类名:int
虚拟机本地方法getName0获取类名:long
虚拟机本地方法getName0获取类名:float
虚拟机本地方法getName0获取类名:double
虚拟机本地方法getName0获取类名:java.lang.Object
虚拟机本地方法getName0获取类名:[I
虚拟机本地方法getName0获取类名:[[I
虚拟机本地方法getName0获取类名:[Ljava.lang.Object;
虚拟机本地方法getName0获取类名:[[Ljava.lang.Object;