用Java实现JVM第九章《本地方法调用》

作者: bugstack虫洞栈 | 来源:发表于2019-05-03 00:14 被阅读2次

    案例介绍
    本章主要介绍用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-08
    ├── 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;
    

    相关文章

      网友评论

        本文标题:用Java实现JVM第九章《本地方法调用》

        本文链接:https://www.haomeiwen.com/subject/utaonqtx.html