16、本地方法调用
大约 2 分钟
本地方法注册与调用
注册
//本地方法定义为函数
type NativeMethod func(frame *rtda.Frame)
//定义函数数组
var registry = map[string]NativeMethod{}
//空方法
func emptyNativeMethod(frame *rtda.Frame) {
// do nothing
}
//注册本地方法
func Register(className, methodName, methodDescriptor string, method NativeMethod) {
key := className + "~" + methodName + "~" + methodDescriptor
registry[key] = method
}
//寻找注册方法
func FindNativeMethod(className, methodName, methodDescriptor string) NativeMethod {
key := className + "~" + methodName + "~" + methodDescriptor
if method, ok := registry[key]; ok {
return method
}
//如果是 object中 registerNatives 返回空方法,且此方法不会注册到registry中
if methodDescriptor == "()V" && methodName == "registerNatives"{
return emptyNativeMethod
}
return nil
}
invoke_native指令
func (self *INVOKE_NATIVE) Execute(frame *rtda.Frame) {
method := frame.Method()
className := method.Class().Name()
methodName := method.Name()
methodDescriptor := method.Descriptor()
nativeMethod := native.FindNativeMethod(className, methodName, methodDescriptor)
if nativeMethod == nil {
methodInfo := className + "." + methodName + methodDescriptor
panic("java.lang.UnsatisfiedLinkError: " + methodInfo)
}
nativeMethod(frame)
}
调用本地方法
method.go
func newMethod(class *Class, cfMethod *classfile.MemberInfo) *Method {
method := &Method{}
method.class = class
method.copyMemberInfo(cfMethod)
method.copyAttributes(cfMethod)
//method.calArgSlotCount() //参数
md := parseMethodDescriptor(method.descriptor)
method.calcArgSlotCount(md.parameterTypes)
if method.IsNative() {
//方法字节码表插入Code
method.injectCodeAttribute(md.returnType)
}
return method
}
//0xfe 对应助记符指令 invoke_native
func (self *Method) injectCodeAttribute(returnType string) {
self.maxStack = 4 // todo
self.maxLocals = self.argSlotCount
switch returnType[0] {
case 'V':
self.code = []byte{0xfe, 0xb1} // return
case 'L', '[':
self.code = []byte{0xfe, 0xb0} // areturn
case 'D':
self.code = []byte{0xfe, 0xaf} // dreturn
case 'F':
self.code = []byte{0xfe, 0xae} // freturn
case 'J':
self.code = []byte{0xfe, 0xad} // lreturn
default:
self.code = []byte{0xfe, 0xac} // ireturn
}
}
添加native类
native/java/lang/Object.go
package lang
import "unsafe"
import "main/native"
import "main/rtda"
const jlObject = "java/lang/Object"
func init() {
native.Register(jlObject, "getClass", "()Ljava/lang/Class;", getClass)
native.Register(jlObject, "hashCode", "()I", hashCode)
native.Register(jlObject, "clone", "()Ljava/lang/Object;", clone)
}
// public final native Class<?> getClass();
// ()Ljava/lang/Class;
func getClass(frame *rtda.Frame) {
this := frame.LocalVars().GetThis()
class := this.Class().JClass()
frame.OperandStack().PushRef(class)
}
// public native int hashCode();
// ()I
func hashCode(frame *rtda.Frame) {
this := frame.LocalVars().GetThis()
hash := int32(uintptr(unsafe.Pointer(this)))
frame.OperandStack().PushInt(hash)
}
// protected native Object clone() throws CloneNotSupportedException;
// ()Ljava/lang/Object;
func clone(frame *rtda.Frame) {
this := frame.LocalVars().GetThis()
cloneable := this.Class().Loader().LoadClass("java/lang/Cloneable")
if !this.Class().IsImplements(cloneable) {
panic("java.lang.CloneNotSupportedException")
}
frame.OperandStack().PushRef(this.Clone())
}
在invoke_native中注册
//todo 非常重要
_ "main/native/java/lang" //@todo init 注册
测试方法
#测试本地方法调用
go run main -cp test/lib/example.jar jvmgo.book.ch09.GetClassTest
go run main -cp test/lib/example.jar jvmgo.book.ch09.StringTest
go run main -cp test/lib/example.jar jvmgo.book.ch09.ObjectTest
go run main -cp test/lib/example.jar jvmgo.book.ch09.CloneTest
实战项目地址
https://gitee.com/yinlingchaoliu/jvmgo.git
提交标签 "native"