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

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

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

}

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
虚拟机本地方法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;