案例介绍
本章主要介绍用java实现一些本地方法类库,并初始化本地方法,之后通过反射命令来调用本地方法。
Java虚拟机和Java类库一起构成了Java运行时环境。Java类库主要用Java语言编写,一些无法用Java语言实现的方法则使用本地语言编写,这额方法叫作本地方法。
OpenJDK类库中的本地方法是用JNI(Java Native Interface)编写的,但是要让虚拟机支持JNI规范还需要大量工作。
环境准备
1、jdk 1.8.0
2、IntelliJ IDEA Community Edition 2018.3.1 x64
配置信息
1、调试配置
2.1、配置位置:Run/Debug Configurations -> program arguments
2.2、配置内容:-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<String, NativeMethod> 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<String, Class> 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<String, Class> 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<String, String> 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;