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:
parent
3500c686b3
commit
f265f1702b
5 changed files with 281 additions and 175 deletions
|
@ -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},
|
||||
|
|
417
runtime/vm.go
417
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)
|
||||
|
||||
|
|
|
@ -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`)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue