跳至主要內容

16、本地方法调用

引领潮流大约 2 分钟手动编写jvm虚拟机jvmgo

本地方法注册与调用

注册

//本地方法定义为函数
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"