diff --git a/vm/class.go b/vm/class.go index afe906c2d..fac772499 100644 --- a/vm/class.go +++ b/vm/class.go @@ -1403,7 +1403,7 @@ var builtinClassCommonInstanceMethods = []*BuiltinMethodObject{ return t.vm.InitErrorObject(errors.InternalError, sourceLine, "%s", args[0].Inspect()) } - return t.vm.InitErrorObject(errorClass.Name, sourceLine, "%s", args[0].Inspect()) + return t.vm.InitErrorObjectFromClass(errorClass, sourceLine, "%s", args[0].Inspect()) case 2: errorClass, ok := args[0].(*RClass) @@ -1411,7 +1411,7 @@ var builtinClassCommonInstanceMethods = []*BuiltinMethodObject{ return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongArgumentTypeFormatNum, 2, "a class", args[0].Class().Name) } - return t.vm.InitErrorObject(errorClass.Name, sourceLine, "%s", args[1].Inspect()) + return t.vm.InitErrorObjectFromClass(errorClass, sourceLine, "%s", args[1].Inspect()) } return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgumentLess, 2, aLen) diff --git a/vm/error.go b/vm/error.go index 0493b0912..793530f58 100644 --- a/vm/error.go +++ b/vm/error.go @@ -22,7 +22,6 @@ type Error struct { message string stackTraces []string storedTraces bool - Type string } // Internal functions =================================================== @@ -35,9 +34,7 @@ func (vm *VM) InitNoMethodError(sourceLine int, methodName string, receiver Obje } // InitErrorObject initializes and returns Error object -func (vm *VM) InitErrorObject(errorType string, sourceLine int, format string, args ...interface{}) *Error { - errClass := vm.objectClass.getClassConstant(errorType) - +func (vm *VM) InitErrorObjectFromClass(errClass *RClass, sourceLine int, format string, args ...interface{}) *Error { t := &vm.mainThread cf := t.callFrameStack.top() @@ -53,17 +50,22 @@ func (vm *VM) InitErrorObject(errorType string, sourceLine int, format string, a return &Error{ BaseObj: NewBaseObject(errClass), // Add 1 to source line because it's zero indexed - message: fmt.Sprintf(errorType+": "+format, args...), + message: fmt.Sprintf(errClass.Name+": "+format, args...), stackTraces: []string{fmt.Sprintf("from %s:%d", cf.FileName(), sourceLine)}, - Type: errorType, } } -func (vm *VM) initErrorClasses() { - errTypes := []string{errors.InternalError, errors.IOError, errors.ArgumentError, errors.NameError, errors.StopIteration, errors.TypeError, errors.NoMethodError, errors.ConstantAlreadyInitializedError, errors.HTTPError, errors.ZeroDivisionError, errors.ChannelCloseError, errors.NotImplementedError} +// InitErrorObject initializes and returns Error object +func (vm *VM) InitErrorObject(errorType errors.ErrorType, sourceLine int, format string, args ...interface{}) *Error { + en := errors.GetErrorName(errorType) + errClass := vm.objectClass.getClassConstant(en) + return vm.InitErrorObjectFromClass(errClass, sourceLine, format, args...) +} - for _, errType := range errTypes { - c := vm.initializeClass(errType) +func (vm *VM) initErrorClasses() { + for _, et := range errors.AllErrorTypes() { + en := errors.GetErrorName(et) + c := vm.initializeClass(en) vm.objectClass.setClassConstant(c) } } diff --git a/vm/errors/error.go b/vm/errors/error.go index 4de9c2f3e..99b1cfd52 100644 --- a/vm/errors/error.go +++ b/vm/errors/error.go @@ -1,32 +1,76 @@ package errors +import "fmt" + +// ErrorType is the enum representation for built-in error types +type ErrorType int8 + const ( // InternalError is the default error type - InternalError = "InternalError" + InternalError ErrorType = iota // IOError is an IO error such as file error - IOError = "IOError" + IOError // ArgumentError is for an argument-related error - ArgumentError = "ArgumentError" + ArgumentError // NameError is for a constant-related error - NameError = "NameError" + NameError // StopIteration is raised when there are no more elements in an iterator - StopIteration = "StopIteration" + StopIteration // TypeError is for a type-related error - TypeError = "TypeError" + TypeError // NoMethodError is for an intentionally unsupported-method error - NoMethodError = "NoMethodError" + NoMethodError // ConstantAlreadyInitializedError means user re-declares twice - ConstantAlreadyInitializedError = "ConstantAlreadyInitializedError" + ConstantAlreadyInitializedError // HTTPError is returned when when a request fails to return a proper response - HTTPError = "HTTPError" + HTTPError // ZeroDivisionError is for zero-division by Integer/Float/Decimal value - ZeroDivisionError = "ZeroDivisionError" + ZeroDivisionError // ChannelCloseError is for accessing to the closed channel - ChannelCloseError = "ChannelCloseError" + ChannelCloseError // NotImplementedError means the method is missing - NotImplementedError = "NotImplementedError" + NotImplementedError + + // This is an anchor for getting all error types' enum values, see AllErrorTypes + EndOfErrorTypeConst ) +var errorTypesMap = map[ErrorType]string{ + InternalError: "InternalError", + IOError: "IOError", + ArgumentError: "ArgumentError", + NameError: "NameError", + StopIteration: "StopIteration", + TypeError: "TypeError", + NoMethodError: "NoMethodError", + ConstantAlreadyInitializedError: "ConstantAlreadyInitializedError", + HTTPError: "HTTPError", + ZeroDivisionError: "ZeroDivisionError", + ChannelCloseError: "ChannelCloseError", + NotImplementedError: "NotImplementedError", +} + + +// AllErrorTypes returns all error types defined in this package in their enum format. +func AllErrorTypes() []ErrorType { + ts := make([]ErrorType, EndOfErrorTypeConst) + for i := 0; i < int(EndOfErrorTypeConst); i++ { + ts[i] = ErrorType(i) + } + return ts +} + +// GetErrorName receives an ErrorType enum and returns the corresponding error name. +func GetErrorName(t ErrorType) string { + v, ok := errorTypesMap[t] + + if ok { + return v + } + + panic(fmt.Errorf("expect to find ErrorType %d's name", t)) +} + /* Here defines different error message formats for different types of errors */ diff --git a/vm/issue_vm.go b/vm/issue_vm.go index 25a7fc0df..f0ab14c30 100644 --- a/vm/issue_vm.go +++ b/vm/issue_vm.go @@ -28,7 +28,7 @@ func PrintError(v *VM) { if !ok { fmt.Println("No error detected") } - fmt.Printf("# %s\n", err.Type) + fmt.Printf("# %s\n", err.Class().Name) fmt.Println(err.Message()) fmt.Printf("### Goby version\n%s\n", Version) diff --git a/vm/thread.go b/vm/thread.go index 7888c2711..3d9e44e43 100644 --- a/vm/thread.go +++ b/vm/thread.go @@ -490,14 +490,14 @@ func (t *Thread) reportArgumentError(sourceLine, idealArgNumber int, methodName } // pushErrorObject pushes the Error object to the stack -func (t *Thread) pushErrorObject(errorType string, sourceLine int, format string, args ...interface{}) { +func (t *Thread) pushErrorObject(errorType errors.ErrorType, sourceLine int, format string, args ...interface{}) { err := t.vm.InitErrorObject(errorType, sourceLine, format, args...) t.Stack.Push(&Pointer{Target: err}) panic(err.Message()) } // setErrorObject replaces a certain stack element with the Error object -func (t *Thread) setErrorObject(receiverPtr, sp int, errorType string, sourceLine int, format string, args ...interface{}) { +func (t *Thread) setErrorObject(receiverPtr, sp int, errorType errors.ErrorType, sourceLine int, format string, args ...interface{}) { err := t.vm.InitErrorObject(errorType, sourceLine, format, args...) t.Stack.Set(receiverPtr, &Pointer{Target: err}) t.Stack.pointer = sp