跳至主要內容

18、异常处理

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

异常处理

unchecked 异常 : java.lang.RuntimeException、java.lang.Error

checked异常:其他

异常父类:java.lang.Throwable

异常指令 athrow

需要实现Throwable native方法

private native Throwable fillInStackTrace(int dummy)

1、注册native方法

const jlThrowable = "java/lang/Throwable"

type StackTraceElement struct {
	fileName   string
	className  string
	methodName string
	lineNumber int
}

func (self *StackTraceElement) String() string {
	return fmt.Sprintf("%s.%s(%s:%d)",
		self.className, self.methodName, self.fileName, self.lineNumber)
}

func init() {
	native.Register(jlThrowable, "fillInStackTrace", "(I)Ljava/lang/Throwable;", fillInStackTrace)
}

// private native Throwable fillInStackTrace(int dummy);
// (I)Ljava/lang/Throwable;
func fillInStackTrace(frame *rtda.Frame) {
	this := frame.LocalVars().GetThis()
	frame.OperandStack().PushRef(this)

	stes := createStackTraceElements(this, frame.Thread())
	this.SetExtra(stes)
}

java代码

void catchOne(){
    try{
        tryItOut()
    }catch(TestExc e){
        handleExc(e)
    }
}

异常处理表

type ExceptionHandler struct {
	startPc   int       //try代码段
	endPc     int       //try代码段
	handlerPc int       //处理句柄
	catchType *ClassRef //异常类
}

Method

type Method struct {
	exceptionTable ExceptionTable //异常处理表
	lineNumberTable * classfile.LineNumberTableAttribute
}

func (self *Method) copyAttributes(cfMethod *classfile.MemberInfo) {
	if codeAttr := cfMethod.CodeAttribute(); codeAttr != nil {
		self.maxStack = codeAttr.MaxStack()
		self.maxLocals = codeAttr.MaxLocals()
		self.code = codeAttr.Code()
		//todo exception 增加异常处理
		self.exceptionTable = newExceptionTable(codeAttr.ExceptionTable(),self.class.constantPool)
		// todo 代码行号
		self.lineNumberTable = codeAttr.LineNumberTableAttribute()
	}
}


func (self *Method) FindExceptionHandler(exClass *Class, pc int) int {
	handler := self.exceptionTable.findExceptionHandler(exClass, pc)
	if handler != nil {
		return handler.handlerPc
	}
	return -1
}

func (self *Method) GetLineNumber(pc int) int {
	if self.IsNative() {
		return -2
	}
	if self.lineNumberTable == nil {
		return -1
	}
	return self.lineNumberTable.GetLineNumber(pc)
}

newExceptionTable

//todo exception 异常处理
type ExceptionTable []*ExceptionHandler

type ExceptionHandler struct {
	startPc   int       //try代码段
	endPc     int       //try代码段
	handlerPc int       //处理句柄
	catchType *ClassRef //异常类
}

//从classfile生成异常处理
func newExceptionTable(entries []*classfile.ExceptionTableEntry, cp *ConstantPool) ExceptionTable {
	table := make([]*ExceptionHandler, len(entries))
	for i, entry := range entries {
		table[i] = &ExceptionHandler{
			startPc:   int(entry.StartPc()),
			endPc:     int(entry.EndPc()),
			handlerPc: int(entry.HandlerPc()),
			catchType: getCatchType(uint(entry.CatchType()), cp),
		}
	}

	return table
}

//获得异常类
func getCatchType(index uint, cp *ConstantPool) *ClassRef {
	if index == 0 {
		return nil // catch all
	}
	return cp.GetConstant(index).(*ClassRef)
}

func (self ExceptionTable) findExceptionHandler(exClass *Class, pc int) *ExceptionHandler {
	for _, handler := range self {
		// jvms: The start_pc is inclusive and end_pc is exclusive
		if pc >= handler.startPc && pc < handler.endPc {
			if handler.catchType == nil {
				return handler //catch-all
			}
			catchClass := handler.catchType.ResolvedClass()
			if catchClass == exClass || catchClass.IsSuperClassOf(exClass) {
				return handler
			}
		}
	}
	return nil
}

异常代码行数

func (self *CodeAttribute) LineNumberTableAttribute() *LineNumberTableAttribute {
	for _, attrInfo := range self.attributes {
		switch attrInfo.(type) {
		case *LineNumberTableAttribute:
			return attrInfo.(*LineNumberTableAttribute)
		}
	}
	return nil
}

异常指令athrow

func (self *ATHROW) Execute(frame *rtda.Frame) {
	ex := frame.OperandStack().PopRef()
	if ex == nil {
		panic("java.lang.NullPointerException")
	}

	thread := frame.Thread()
	//未找异常处理方法
	if !findAndGotoExceptionHandler(thread, ex) {
		//抛出uncaught异常
		handleUncaughtException(thread, ex)
	}
}

func findAndGotoExceptionHandler(thread *rtda.Thread, ex *heap.Object) bool {
	for {
		frame := thread.CurrentFrame()
		pc := frame.NextPC() - 1

		handlerPC := frame.Method().FindExceptionHandler(ex.Class(), pc)
		if handlerPC > 0 {
			stack := frame.OperandStack()
			stack.Clear()
			stack.PushRef(ex)
			frame.SetNextPC(handlerPC)
			return true
		}

		thread.PopFrame()
		if thread.IsStackEmpty() {
			break
		}
	}
	return false
}

// todo
func handleUncaughtException(thread *rtda.Thread, ex *heap.Object) {
	thread.ClearStack()

	jMsg := ex.GetRefVar("detailMessage", "Ljava/lang/String;")
	goMsg := heap.GoString(jMsg)
	println(ex.Class().JavaName() + ": " + goMsg)

	stes := reflect.ValueOf(ex.Extra())
	for i := 0; i < stes.Len(); i++ {
		ste := stes.Index(i).Interface().(interface {
			String() string
		})
		println("	at " + ste.String())
	}
}

异常测试

#exception 异常处理
go run main   -cp test/lib/example.jar   jvmgo.book.ch10.ParseIntTest  123
go run main   -cp test/lib/example.jar   jvmgo.book.ch10.ParseIntTest  abc
go run main   -cp test/lib/example.jar   jvmgo.book.ch10.ParseIntTest

实战项目地址

https://gitee.com/yinlingchaoliu/jvmgo.git

提交标签 "exception"