From f265f1702b93e762a4466cb6445ab6f4247f79e7 Mon Sep 17 00:00:00 2001 From: earncef Date: Thu, 21 Feb 2019 18:40:17 +0100 Subject: [PATCH] Stack trace for errors (#108) * Simple strack trace for runtime errors. * Module test for runtime error stack trace * Removed OpExport * Removed redundant code. * Removed vm.FrameInfo() --- compiler/opcodes.go | 3 - runtime/vm.go | 417 +++++++++++++++++++++++--------------- runtime/vm_call_test.go | 12 ++ runtime/vm_module_test.go | 15 +- runtime/vm_test.go | 9 - 5 files changed, 281 insertions(+), 175 deletions(-) diff --git a/compiler/opcodes.go b/compiler/opcodes.go index 7a79511..d4cf1ba 100644 --- a/compiler/opcodes.go +++ b/compiler/opcodes.go @@ -41,7 +41,6 @@ const ( OpCall // Call function OpReturn // Return OpReturnValue // Return value - OpExport // Export OpGetGlobal // Get global variable OpSetGlobal // Set global variable OpSetSelGlobal // Set global variable using selectors @@ -102,7 +101,6 @@ var OpcodeNames = [...]string{ OpCall: "CALL", OpReturn: "RET", OpReturnValue: "RETVAL", - OpExport: "EXPORT", OpGetLocal: "GETL", OpSetLocal: "SETL", OpDefineLocal: "DEFL", @@ -160,7 +158,6 @@ var OpcodeOperands = [...][]int{ OpCall: {1}, OpReturn: {}, OpReturnValue: {}, - OpExport: {}, OpGetLocal: {1}, OpSetLocal: {1}, OpDefineLocal: {1}, diff --git a/runtime/vm.go b/runtime/vm.go index 9d1e9db..5bbcf94 100644 --- a/runtime/vm.go +++ b/runtime/vm.go @@ -84,7 +84,7 @@ func (v *VM) Abort() { } // Run starts the execution. -func (v *VM) Run() error { +func (v *VM) Run() (err error) { // reset VM states v.sp = 0 v.curFrame = &(v.frames[0]) @@ -104,7 +104,8 @@ mainloop: v.ip += 2 if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &v.constants[cidx] @@ -112,7 +113,8 @@ mainloop: case compiler.OpNull: if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = undefinedPtr @@ -123,19 +125,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Add, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Add, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s + %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s + %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -146,19 +151,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Sub, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Sub, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s - %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s - %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -169,19 +177,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Mul, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Mul, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s * %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s * %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -192,19 +203,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Quo, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Quo, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s / %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s / %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -215,19 +229,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Rem, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Rem, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s %% %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s %% %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -238,19 +255,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.And, *right) - if err != nil { + res, e := (*left).BinaryOp(token.And, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s & %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s & %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -261,19 +281,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Or, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Or, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s | %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s | %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -284,19 +307,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Xor, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Xor, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s ^ %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s ^ %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -307,19 +333,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.AndNot, *right) - if err != nil { + res, e := (*left).BinaryOp(token.AndNot, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s &^ %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s &^ %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -330,19 +359,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Shl, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Shl, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s << %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s << %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -353,19 +385,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Shr, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Shr, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s >> %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s >> %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -377,7 +412,8 @@ mainloop: v.sp -= 2 if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } if (*left).Equals(*right) { @@ -393,7 +429,8 @@ mainloop: v.sp -= 2 if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } if (*left).Equals(*right) { @@ -408,19 +445,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.Greater, *right) - if err != nil { + res, e := (*left).BinaryOp(token.Greater, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s > %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s > %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -431,19 +471,22 @@ mainloop: left := v.stack[v.sp-2] v.sp -= 2 - res, err := (*left).BinaryOp(token.GreaterEq, *right) - if err != nil { + res, e := (*left).BinaryOp(token.GreaterEq, *right) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidOperator { - return fmt.Errorf("%s: invalid operation: %s >= %s", + if e == objects.ErrInvalidOperator { + err = fmt.Errorf("%s: invalid operation: %s >= %s", filePos, (*left).TypeName(), (*right).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &res @@ -454,7 +497,8 @@ mainloop: case compiler.OpTrue: if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = truePtr @@ -462,7 +506,8 @@ mainloop: case compiler.OpFalse: if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = falsePtr @@ -473,7 +518,8 @@ mainloop: v.sp-- if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } if (*operand).IsFalsy() { @@ -490,7 +536,8 @@ mainloop: switch x := (*operand).(type) { case *objects.Int: if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var res objects.Object = &objects.Int{Value: ^x.Value} @@ -499,7 +546,8 @@ mainloop: v.sp++ default: filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid operation: ^%s", filePos, (*operand).TypeName()) + err = fmt.Errorf("%s: invalid operation: ^%s", filePos, (*operand).TypeName()) + break mainloop } case compiler.OpMinus: @@ -509,7 +557,8 @@ mainloop: switch x := (*operand).(type) { case *objects.Int: if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var res objects.Object = &objects.Int{Value: -x.Value} @@ -518,7 +567,8 @@ mainloop: v.sp++ case *objects.Float: if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var res objects.Object = &objects.Float{Value: -x.Value} @@ -527,7 +577,8 @@ mainloop: v.sp++ default: filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid operation: -%s", filePos, (*operand).TypeName()) + err = fmt.Errorf("%s: invalid operation: -%s", filePos, (*operand).TypeName()) + break mainloop } case compiler.OpJumpFalsy: @@ -585,9 +636,10 @@ mainloop: val := v.stack[v.sp-numSelectors-1] v.sp -= numSelectors + 1 - if err := indexAssign(v.globals[globalIndex], val, selectors); err != nil { + if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-3]) - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } case compiler.OpGetGlobal: @@ -597,7 +649,8 @@ mainloop: val := v.globals[globalIndex] if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = val @@ -616,7 +669,8 @@ mainloop: var arr objects.Object = &objects.Array{Value: elements} if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &arr @@ -637,7 +691,8 @@ mainloop: var m objects.Object = &objects.Map{Value: kv} if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &m @@ -646,11 +701,11 @@ mainloop: case compiler.OpError: value := v.stack[v.sp-1] - var err objects.Object = &objects.Error{ + var e objects.Object = &objects.Error{ Value: *value, } - v.stack[v.sp-1] = &err + v.stack[v.sp-1] = &e case compiler.OpImmutable: value := v.stack[v.sp-1] @@ -675,36 +730,41 @@ mainloop: switch left := (*left).(type) { case objects.Indexable: - val, err := left.IndexGet(*index) - if err != nil { + val, e := left.IndexGet(*index) + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - if err == objects.ErrInvalidIndexType { - return fmt.Errorf("%s: invalid index type: %s", filePos, (*index).TypeName()) + if e == objects.ErrInvalidIndexType { + err = fmt.Errorf("%s: invalid index type: %s", filePos, (*index).TypeName()) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } if val == nil { val = objects.UndefinedValue } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &val v.sp++ - case *objects.Error: // err.value + case *objects.Error: // e.value key, ok := (*index).(*objects.String) if !ok || key.Value != "value" { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid index on error", filePos) + err = fmt.Errorf("%s: invalid index on error", filePos) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &left.Value @@ -712,7 +772,8 @@ mainloop: default: filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: not indexable: %s", filePos, left.TypeName()) + err = fmt.Errorf("%s: not indexable: %s", filePos, left.TypeName()) + break mainloop } case compiler.OpSliceIndex: @@ -727,7 +788,8 @@ mainloop: lowIdx = low.Value } else { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index type: %s", filePos, low.TypeName()) + err = fmt.Errorf("%s: invalid slice index type: %s", filePos, low.TypeName()) + break mainloop } } @@ -741,12 +803,14 @@ mainloop: highIdx = high.Value } else { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + err = fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + break mainloop } if lowIdx > highIdx { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + err = fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + break mainloop } if lowIdx < 0 { @@ -762,7 +826,8 @@ mainloop: } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} @@ -778,12 +843,14 @@ mainloop: highIdx = high.Value } else { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + err = fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + break mainloop } if lowIdx > highIdx { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + err = fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + break mainloop } if lowIdx < 0 { @@ -799,7 +866,8 @@ mainloop: } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} @@ -816,12 +884,14 @@ mainloop: highIdx = high.Value } else { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + err = fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + break mainloop } if lowIdx > highIdx { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + err = fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + break mainloop } if lowIdx < 0 { @@ -837,7 +907,8 @@ mainloop: } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]} @@ -854,12 +925,14 @@ mainloop: highIdx = high.Value } else { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + err = fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName()) + break mainloop } if lowIdx > highIdx { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + err = fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx) + break mainloop } if lowIdx < 0 { @@ -875,7 +948,8 @@ mainloop: } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]} @@ -894,8 +968,9 @@ mainloop: case *objects.Closure: if numArgs != callee.Fn.NumParameters { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1]) - return fmt.Errorf("%s: wrong number of arguments: want=%d, got=%d", + err = fmt.Errorf("%s: wrong number of arguments: want=%d, got=%d", filePos, callee.Fn.NumParameters, numArgs) + break mainloop } // test if it's tail-call @@ -927,8 +1002,9 @@ mainloop: case *objects.CompiledFunction: if numArgs != callee.NumParameters { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1]) - return fmt.Errorf("%s: wrong number of arguments: want=%d, got=%d", + err = fmt.Errorf("%s: wrong number of arguments: want=%d, got=%d", filePos, callee.NumParameters, numArgs) + break mainloop } // test if it's tail-call @@ -963,24 +1039,27 @@ mainloop: args = append(args, *arg) } - ret, err := callee.Call(args...) + ret, e := callee.Call(args...) v.sp -= numArgs + 1 // runtime error - if err != nil { + if e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1]) - if err == objects.ErrWrongNumArguments { - return fmt.Errorf("%s: wrong number of arguments in call to '%s'", + if e == objects.ErrWrongNumArguments { + err = fmt.Errorf("%s: wrong number of arguments in call to '%s'", filePos, value.TypeName()) + break mainloop } - if err, ok := err.(objects.ErrInvalidArgumentType); ok { - return fmt.Errorf("%s: invalid type for argument '%s' in call to '%s': expected %s, found %s", - filePos, err.Name, value.TypeName(), err.Expected, err.Found) + if e, ok := e.(objects.ErrInvalidArgumentType); ok { + err = fmt.Errorf("%s: invalid type for argument '%s' in call to '%s': expected %s, found %s", + filePos, e.Name, value.TypeName(), e.Expected, e.Found) + break mainloop } - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } // nil return -> undefined @@ -989,7 +1068,8 @@ mainloop: } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &ret @@ -997,7 +1077,8 @@ mainloop: default: filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1]) - return fmt.Errorf("%s: not callable: %s", filePos, callee.TypeName()) + err = fmt.Errorf("%s: not callable: %s", filePos, callee.TypeName()) + break mainloop } case compiler.OpReturnValue: @@ -1015,10 +1096,6 @@ mainloop: v.sp = lastFrame.basePointer // skip stack overflow check because (newSP) <= (oldSP) - //if v.sp-1 >= StackSize { - // return ErrStackOverflow - //} - v.stack[v.sp-1] = retVal //v.sp++ @@ -1034,10 +1111,6 @@ mainloop: v.sp = lastFrame.basePointer // skip stack overflow check because (newSP) <= (oldSP) - //if v.sp-1 >= StackSize { - // return ErrStackOverflow - //} - v.stack[v.sp-1] = undefinedPtr //v.sp++ @@ -1079,9 +1152,10 @@ mainloop: sp := v.curFrame.basePointer + localIndex - if err := indexAssign(v.stack[sp], val, selectors); err != nil { + if e := indexAssign(v.stack[sp], val, selectors); e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-2]) - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } case compiler.OpGetLocal: @@ -1091,7 +1165,8 @@ mainloop: val := v.stack[v.curFrame.basePointer+localIndex] if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = val @@ -1102,7 +1177,8 @@ mainloop: v.ip++ if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &builtinFuncs[builtinIndex] @@ -1117,11 +1193,13 @@ mainloop: module, ok := v.builtinModules[moduleName] if !ok { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-3]) - return fmt.Errorf("%s: module '%s' not found", filePos, moduleName) + err = fmt.Errorf("%s: module '%s' not found", filePos, moduleName) + break mainloop } if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = module @@ -1135,7 +1213,8 @@ mainloop: fn, ok := v.constants[constIndex].(*objects.CompiledFunction) if !ok { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-3]) - return fmt.Errorf("%s: not function: %s", filePos, fn.TypeName()) + err = fmt.Errorf("%s: not function: %s", filePos, fn.TypeName()) + break mainloop } free := make([]*objects.Object, numFree) @@ -1145,7 +1224,8 @@ mainloop: v.sp -= numFree if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } var cl objects.Object = &objects.Closure{ @@ -1163,7 +1243,8 @@ mainloop: val := v.curFrame.freeVars[freeIndex] if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = val @@ -1179,9 +1260,10 @@ mainloop: val := v.stack[v.sp-numSelectors-1] v.sp -= numSelectors + 1 - if err := indexAssign(v.curFrame.freeVars[freeIndex], val, selectors); err != nil { + if e := indexAssign(v.curFrame.freeVars[freeIndex], val, selectors); e != nil { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-2]) - return fmt.Errorf("%s: %s", filePos, err.Error()) + err = fmt.Errorf("%s: %s", filePos, e.Error()) + break mainloop } case compiler.OpSetFree: @@ -1202,13 +1284,15 @@ mainloop: iterable, ok := (*dst).(objects.Iterable) if !ok { filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip]) - return fmt.Errorf("%s: not iterable: %s", filePos, (*dst).TypeName()) + err = fmt.Errorf("%s: not iterable: %s", filePos, (*dst).TypeName()) + break mainloop } iterator = iterable.Iterate() if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &iterator @@ -1221,7 +1305,8 @@ mainloop: hasMore := (*iterator).(objects.Iterator).Next() if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } if hasMore { @@ -1238,7 +1323,8 @@ mainloop: val := (*iterator).(objects.Iterator).Key() if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &val @@ -1251,7 +1337,8 @@ mainloop: val := (*iterator).(objects.Iterator).Value() if v.sp >= StackSize { - return ErrStackOverflow + err = ErrStackOverflow + break mainloop } v.stack[v.sp] = &val @@ -1262,6 +1349,17 @@ mainloop: } } + if err != nil { + for v.framesIndex > 1 { + v.framesIndex-- + v.curFrame = &v.frames[v.framesIndex-1] + + filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.curFrame.ip-1]) + err = fmt.Errorf("%s\nin %s", err.Error(), filePos) + } + return err + } + // check if stack still has some objects left if v.sp > 0 && atomic.LoadInt64(&v.aborting) == 0 { panic(fmt.Errorf("non empty stack after execution: %d", v.sp)) @@ -1275,11 +1373,6 @@ func (v *VM) Globals() []*objects.Object { return v.globals } -// FrameInfo returns the current function call frame information. -func (v *VM) FrameInfo() (frameIndex, ip int) { - return v.framesIndex - 1, v.ip -} - func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error { numSel := len(selectors) diff --git a/runtime/vm_call_test.go b/runtime/vm_call_test.go index ceef69b..1bec423 100644 --- a/runtime/vm_call_test.go +++ b/runtime/vm_call_test.go @@ -6,4 +6,16 @@ func TestCall(t *testing.T) { expect(t, `a := { b: func(x) { return x + 2 } }; out = a.b(5)`, 7) expect(t, `a := { b: { c: func(x) { return x + 2 } } }; out = a.b.c(5)`, 7) expect(t, `a := { b: { c: func(x) { return x + 2 } } }; out = a["b"].c(5)`, 7) + expectError(t, `a := 1 +b := func(a, c) { + c(a) +} + +c := func(a) { + a() +} +b(a, c) +`, `test:7:4: not callable: int +in test:3:4 +in test:9:1`) } diff --git a/runtime/vm_module_test.go b/runtime/vm_module_test.go index c826fd5..4e6c3dc 100644 --- a/runtime/vm_module_test.go +++ b/runtime/vm_module_test.go @@ -53,7 +53,7 @@ os.remove("./temp") expect(t, ` os := import("os") cmd := os.exec("echo", "foo", "bar") -if !is_error(cmd) { +if !is_error(cmd) { out = cmd.output() } `, []byte("foo bar\n")) @@ -207,6 +207,19 @@ export func() { expectErrorWithUserModules(t, `a := 5; import("mod1")`, map[string]string{ "mod1": `export a`, }, "mod1:1:8: unresolved reference 'a'") + + // runtime error within modules + expectErrorWithUserModules(t, ` +a := 1; +b := import("mod1"); +b(a)`, + map[string]string{"mod1": ` +export func(a) { + a() +} +`, + }, `mod1:3:4: not callable: int +in test:4:1`) } func TestModuleBlockScopes(t *testing.T) { diff --git a/runtime/vm_test.go b/runtime/vm_test.go index 5524b46..b11b4b6 100644 --- a/runtime/vm_test.go +++ b/runtime/vm_test.go @@ -202,12 +202,6 @@ func traceCompileRun(file *ast.File, symbols map[string]objects.Object, userModu stackTrace = append(stackTrace, fmt.Sprintf(" %s:%d", file, line)) } - var ipstr string - if v != nil { - frameIdx, ip := v.FrameInfo() - ipstr = fmt.Sprintf("\n (Frame=%d, IP=%d)", frameIdx, ip+1) - } - trace = append(trace, fmt.Sprintf("[Panic]\n\n %v%s\n", e, ipstr)) trace = append(trace, fmt.Sprintf("[Error Trace]\n\n %s\n", strings.Join(stackTrace, "\n "))) } }() @@ -261,9 +255,6 @@ func traceCompileRun(file *ast.File, symbols map[string]objects.Object, userModu res[name] = *globals[sym.Index] } trace = append(trace, fmt.Sprintf("\n[Globals]\n\n%s", strings.Join(formatGlobals(globals), "\n"))) - - frameIdx, ip := v.FrameInfo() - trace = append(trace, fmt.Sprintf("\n[IP]\n\nFrame=%d, IP=%d", frameIdx, ip+1)) } if err != nil { return