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()
This commit is contained in:
earncef 2019-02-21 18:40:17 +01:00 committed by Daniel
parent 3500c686b3
commit f265f1702b
5 changed files with 281 additions and 175 deletions

View file

@ -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},

View file

@ -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)

View file

@ -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`)
}

View file

@ -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) {

View file

@ -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