From 2685a42bc060e5ae2da3d7583eecc35dd90e6902 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Wed, 5 Mar 2025 17:58:01 +0100 Subject: [PATCH] proc: do not pin function call returns for toplevel call (#3925) For the toplevel function call do not perform pinning since the function results are unpinned immediately. --- pkg/proc/evalop/evalcompile.go | 176 ++++++++++++++++++++------------- pkg/proc/evalop/ops.go | 2 + pkg/proc/fncall.go | 10 +- pkg/proc/variables_test.go | 78 +++++++++------ service/dap/server_test.go | 2 +- 5 files changed, 165 insertions(+), 103 deletions(-) diff --git a/pkg/proc/evalop/evalcompile.go b/pkg/proc/evalop/evalcompile.go index 7fdded01..7cac717d 100644 --- a/pkg/proc/evalop/evalcompile.go +++ b/pkg/proc/evalop/evalcompile.go @@ -27,7 +27,8 @@ type compileCtx struct { allowCalls bool curCall int flags Flags - firstCall bool + pinnerUsed bool + hasCalls bool } type evalLookup interface { @@ -45,13 +46,13 @@ const ( // CompileAST compiles the expression t into a list of instructions. func CompileAST(lookup evalLookup, t ast.Expr, flags Flags) ([]Op, error) { - ctx := &compileCtx{evalLookup: lookup, allowCalls: true, flags: flags, firstCall: true} - err := ctx.compileAST(t) + ctx := &compileCtx{evalLookup: lookup, allowCalls: true, flags: flags} + err := ctx.compileAST(t, true) if err != nil { return nil, err } - ctx.compileDebugUnpin() + ctx.compileDebugPinnerSetupTeardown() err = ctx.depthCheck(1) if err != nil { @@ -96,23 +97,25 @@ func CompileSet(lookup evalLookup, lhexpr, rhexpr string, flags Flags) ([]Op, er return nil, err } - ctx := &compileCtx{evalLookup: lookup, allowCalls: true, flags: flags, firstCall: true} - err = ctx.compileAST(rhe) + ctx := &compileCtx{evalLookup: lookup, allowCalls: true, flags: flags} + err = ctx.compileAST(rhe, false) if err != nil { return nil, err } - if isStringLiteral(rhe) { + if isStringLiteralThatNeedsAlloc(rhe) { ctx.compileAllocLiteralString() } - err = ctx.compileAST(lhe) + err = ctx.compileAST(lhe, false) if err != nil { return nil, err } ctx.pushOp(&SetValue{lhe: lhe, Rhe: rhe}) + ctx.compileDebugPinnerSetupTeardown() + err = ctx.depthCheck(0) if err != nil { return ctx.ops, err @@ -132,23 +135,31 @@ func (ctx *compileCtx) compileAllocLiteralString() { &PushLen{}, &PushNil{}, &PushConst{constant.MakeBool(false)}, - }, true) + }, specialCallDoPinning|specialCallIsStringAlloc) ctx.pushOp(&ConvertAllocToString{}) jmp.Target = len(ctx.ops) } -func (ctx *compileCtx) compileSpecialCall(fnname string, argAst []ast.Expr, args []Op, doPinning bool) { - if doPinning { - ctx.compileGetDebugPinner() - } +type specialCallFlags uint8 + +const ( + specialCallDoPinning specialCallFlags = 1 << iota + specialCallIsStringAlloc + specialCallComplainAboutStringAlloc +) + +func (ctx *compileCtx) compileSpecialCall(fnname string, argAst []ast.Expr, args []Op, flags specialCallFlags) { + doPinning := flags&specialCallDoPinning != 0 id := ctx.curCall ctx.curCall++ ctx.pushOp(&CallInjectionStartSpecial{ id: id, FnName: fnname, - ArgAst: argAst}) + ArgAst: argAst, + + ComplainAboutStringAlloc: flags&specialCallComplainAboutStringAlloc != 0}) ctx.pushOp(&CallInjectionSetTarget{id: id}) for i := range args { @@ -160,6 +171,13 @@ func (ctx *compileCtx) compileSpecialCall(fnname string, argAst []ast.Expr, args doPinning = doPinning && (ctx.flags&HasDebugPinner != 0) + if doPinning { + ctx.pinnerUsed = true + if flags&specialCallIsStringAlloc == 0 { + ctx.hasCalls = true + } + } + ctx.pushOp(&CallInjectionComplete{id: id, DoPinning: doPinning}) if doPinning { @@ -167,25 +185,40 @@ func (ctx *compileCtx) compileSpecialCall(fnname string, argAst []ast.Expr, args } } -func (ctx *compileCtx) compileGetDebugPinner() { - if ctx.firstCall && ctx.flags&HasDebugPinner != 0 { - ctx.compileSpecialCall(DebugPinnerFunctionName, []ast.Expr{}, []Op{}, false) - ctx.pushOp(&SetDebugPinner{}) - ctx.firstCall = false +func (ctx *compileCtx) compileDebugPinnerSetupTeardown() { + if !ctx.pinnerUsed { + return } -} -func (ctx *compileCtx) compileDebugUnpin() { - if !ctx.firstCall && ctx.flags&HasDebugPinner != 0 { - ctx.compileSpecialCall("runtime.(*Pinner).Unpin", []ast.Expr{ - &ast.Ident{Name: "debugPinner"}, - }, []Op{ - &PushDebugPinner{}, - }, false) - ctx.pushOp(&Pop{}) - ctx.pushOp(&PushNil{}) - ctx.pushOp(&SetDebugPinner{}) + // Prepend debug pinner allocation + mainops := ctx.ops + ctx.ops = []Op{} + flags := specialCallFlags(0) + if !ctx.hasCalls { + flags = specialCallComplainAboutStringAlloc } + ctx.compileSpecialCall(DebugPinnerFunctionName, []ast.Expr{}, []Op{}, flags) + ctx.pushOp(&SetDebugPinner{}) + + // Adjust jump destinations + for _, op := range mainops { + switch op := op.(type) { + case *Jump: + op.Target += len(ctx.ops) + } + } + + ctx.ops = append(ctx.ops, mainops...) + + // Append debug pinner deallocation + ctx.compileSpecialCall("runtime.(*Pinner).Unpin", []ast.Expr{ + &ast.Ident{Name: "debugPinner"}, + }, []Op{ + &PushDebugPinner{}, + }, 0) + ctx.pushOp(&Pop{}) + ctx.pushOp(&PushNil{}) + ctx.pushOp(&SetDebugPinner{}) } func (ctx *compileCtx) pushOp(op Op) { @@ -247,17 +280,17 @@ func (ctx *compileCtx) depthCheck(endDepth int) error { return nil } -func (ctx *compileCtx) compileAST(t ast.Expr) error { +func (ctx *compileCtx) compileAST(t ast.Expr, toplevel bool) error { switch node := t.(type) { case *ast.CallExpr: - return ctx.compileTypeCastOrFuncCall(node) + return ctx.compileTypeCastOrFuncCall(node, toplevel) case *ast.Ident: return ctx.compileIdent(node) case *ast.ParenExpr: // otherwise just eval recursively - return ctx.compileAST(node.X) + return ctx.compileAST(node.X, false) case *ast.SelectorExpr: // . switch x := node.X.(type) { @@ -367,10 +400,10 @@ func (ctx *compileCtx) compileAST(t ast.Expr) error { return nil } -func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr) error { +func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr, toplevel bool) error { if len(node.Args) != 1 { // Things that have more or less than one argument are always function calls. - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) } ambiguous := func() error { @@ -378,9 +411,9 @@ func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr) error { // evaluated then try to treat it as a function call, otherwise try the // type cast. ctx2 := &compileCtx{evalLookup: ctx.evalLookup} - err0 := ctx2.compileAST(node.Fun) + err0 := ctx2.compileAST(node.Fun, false) if err0 == nil { - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) } return ctx.compileTypeCast(node, err0) } @@ -408,12 +441,12 @@ func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr) error { } return ambiguous() } - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) case *ast.Ident: if typ, _ := ctx.FindTypeExpr(n); typ != nil { return ctx.compileTypeCast(node, fmt.Errorf("could not find symbol value for %s", n.Name)) } - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) case *ast.IndexExpr: // Ambiguous, could be a parametric type switch n.X.(type) { @@ -423,20 +456,20 @@ func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr) error { if err == nil || err != reader.ErrTypeNotFound { return err } - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) default: - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) } case *ast.IndexListExpr: return ctx.compileTypeCast(node, nil) default: // All other expressions must be function calls - return ctx.compileFunctionCall(node) + return ctx.compileFunctionCall(node, toplevel) } } func (ctx *compileCtx) compileTypeCast(node *ast.CallExpr, ambiguousErr error) error { - err := ctx.compileAST(node.Args[0]) + err := ctx.compileAST(node.Args[0], false) if err != nil { return err } @@ -468,7 +501,7 @@ func (ctx *compileCtx) compileTypeCast(node *ast.CallExpr, ambiguousErr error) e func (ctx *compileCtx) compileBuiltinCall(builtin string, args []ast.Expr) error { for _, arg := range args { - err := ctx.compileAST(arg) + err := ctx.compileAST(arg, false) if err != nil { return err } @@ -483,7 +516,7 @@ func (ctx *compileCtx) compileIdent(node *ast.Ident) error { } func (ctx *compileCtx) compileUnary(expr ast.Expr, op Op) error { - err := ctx.compileAST(expr) + err := ctx.compileAST(expr, false) if err != nil { return err } @@ -492,7 +525,7 @@ func (ctx *compileCtx) compileUnary(expr ast.Expr, op Op) error { } func (ctx *compileCtx) compileTypeAssert(node *ast.TypeAssertExpr) error { - err := ctx.compileAST(node.X) + err := ctx.compileAST(node.X, false) if err != nil { return err } @@ -512,14 +545,14 @@ func (ctx *compileCtx) compileTypeAssert(node *ast.TypeAssertExpr) error { } func (ctx *compileCtx) compileBinary(a, b ast.Expr, sop *Jump, op Op) error { - err := ctx.compileAST(a) + err := ctx.compileAST(a, false) if err != nil { return err } if sop != nil { ctx.pushOp(sop) } - err = ctx.compileAST(b) + err = ctx.compileAST(b, false) if err != nil { return err } @@ -528,7 +561,7 @@ func (ctx *compileCtx) compileBinary(a, b ast.Expr, sop *Jump, op Op) error { } func (ctx *compileCtx) compileReslice(node *ast.SliceExpr) error { - err := ctx.compileAST(node.X) + err := ctx.compileAST(node.X, false) if err != nil { return err } @@ -537,7 +570,7 @@ func (ctx *compileCtx) compileReslice(node *ast.SliceExpr) error { hasHigh := false if node.High != nil { hasHigh = true - err = ctx.compileAST(node.High) + err = ctx.compileAST(node.High, false) if err != nil { return err } @@ -548,7 +581,7 @@ func (ctx *compileCtx) compileReslice(node *ast.SliceExpr) error { } if node.Low != nil { - err = ctx.compileAST(node.Low) + err = ctx.compileAST(node.Low, false) if err != nil { return err } @@ -562,7 +595,7 @@ func (ctx *compileCtx) compileReslice(node *ast.SliceExpr) error { return nil } -func (ctx *compileCtx) compileFunctionCall(node *ast.CallExpr) error { +func (ctx *compileCtx) compileFunctionCall(node *ast.CallExpr, toplevel bool) error { if fnnode, ok := node.Fun.(*ast.Ident); ok { if ctx.HasBuiltin(fnnode.Name) { return ctx.compileBuiltinCall(fnnode.Name, node.Args) @@ -576,7 +609,7 @@ func (ctx *compileCtx) compileFunctionCall(node *ast.CallExpr) error { ctx.curCall++ if ctx.flags&HasDebugPinner != 0 { - return ctx.compileFunctionCallWithPinning(node, id) + return ctx.compileFunctionCallWithPinning(node, id, toplevel) } return ctx.compileFunctionCallNoPinning(node, id) @@ -588,7 +621,7 @@ func (ctx *compileCtx) compileFunctionCallNoPinning(node *ast.CallExpr, id int) oldAllowCalls := ctx.allowCalls oldOps := ctx.ops ctx.allowCalls = false - err := ctx.compileAST(node.Fun) + err := ctx.compileAST(node.Fun, false) ctx.allowCalls = oldAllowCalls hasFunc := false if err != nil { @@ -608,7 +641,7 @@ func (ctx *compileCtx) compileFunctionCallNoPinning(node *ast.CallExpr, id int) ctx.pushOp(jmpif) } ctx.pushOp(&Pop{}) - err = ctx.compileAST(node.Fun) + err = ctx.compileAST(node.Fun, false) if err != nil { return err } @@ -619,11 +652,11 @@ func (ctx *compileCtx) compileFunctionCallNoPinning(node *ast.CallExpr, id int) ctx.pushOp(&CallInjectionSetTarget{id: id}) for i, arg := range node.Args { - err := ctx.compileAST(arg) + err := ctx.compileAST(arg, false) if err != nil { return fmt.Errorf("error evaluating %q as argument %d in function %s: %v", astutil.ExprToString(arg), i+1, astutil.ExprToString(node.Fun), err) } - if isStringLiteral(arg) { + if isStringLiteralThatNeedsAlloc(arg) { ctx.compileAllocLiteralString() } ctx.pushOp(&CallInjectionCopyArg{id: id, ArgNum: i, ArgExpr: arg}) @@ -636,17 +669,20 @@ func (ctx *compileCtx) compileFunctionCallNoPinning(node *ast.CallExpr, id int) // compileFunctionCallWithPinning compiles a function call when runtime.debugPinner // is available in the target. -func (ctx *compileCtx) compileFunctionCallWithPinning(node *ast.CallExpr, id int) error { - ctx.compileGetDebugPinner() +func (ctx *compileCtx) compileFunctionCallWithPinning(node *ast.CallExpr, id int, toplevel bool) error { + if !toplevel { + ctx.pinnerUsed = true + } + ctx.hasCalls = true - err := ctx.compileAST(node.Fun) + err := ctx.compileAST(node.Fun, false) if err != nil { return err } for i, arg := range node.Args { - err := ctx.compileAST(arg) - if isStringLiteral(arg) { + err := ctx.compileAST(arg, false) + if isStringLiteralThatNeedsAlloc(arg) { ctx.compileAllocLiteralString() } if err != nil { @@ -664,9 +700,11 @@ func (ctx *compileCtx) compileFunctionCallWithPinning(node *ast.CallExpr, id int ctx.pushOp(&CallInjectionCopyArg{id: id, ArgNum: i, ArgExpr: arg}) } - ctx.pushOp(&CallInjectionComplete{id: id, DoPinning: true}) + ctx.pushOp(&CallInjectionComplete{id: id, DoPinning: !toplevel}) - ctx.compilePinningLoop(id) + if !toplevel { + ctx.compilePinningLoop(id) + } return nil } @@ -682,7 +720,7 @@ func (ctx *compileCtx) compilePinningLoop(id int) { }, []Op{ &PushDebugPinner{}, nil, - }, false) + }, 0) ctx.pushOp(&Pop{}) ctx.pushOp(&Jump{When: JumpAlways, Target: loopStart}) jmp.Target = len(ctx.ops) @@ -700,16 +738,16 @@ func Listing(depth []int, ops []Op) string { return buf.String() } -func isStringLiteral(expr ast.Expr) bool { +func isStringLiteralThatNeedsAlloc(expr ast.Expr) bool { switch expr := expr.(type) { case *ast.BasicLit: - return expr.Kind == token.STRING + return expr.Kind == token.STRING && expr.Value != `""` case *ast.BinaryExpr: if expr.Op == token.ADD { - return isStringLiteral(expr.X) && isStringLiteral(expr.Y) + return isStringLiteralThatNeedsAlloc(expr.X) && isStringLiteralThatNeedsAlloc(expr.Y) } case *ast.ParenExpr: - return isStringLiteral(expr.X) + return isStringLiteralThatNeedsAlloc(expr.X) } return false } diff --git a/pkg/proc/evalop/ops.go b/pkg/proc/evalop/ops.go index d64c0995..f245abbe 100644 --- a/pkg/proc/evalop/ops.go +++ b/pkg/proc/evalop/ops.go @@ -285,6 +285,8 @@ type CallInjectionStartSpecial struct { id int FnName string ArgAst []ast.Expr + + ComplainAboutStringAlloc bool // if this call injection can not be made complain specifically about string allocation } func (*CallInjectionStartSpecial) depthCheck() (npop, npush int) { return 0, 1 } diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index 2fd0cace..9b4f8c85 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -1062,10 +1062,18 @@ func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64 } func (scope *EvalScope) callInjectionStartSpecial(stack *evalStack, op *evalop.CallInjectionStartSpecial, curthread Thread) bool { + if op.ComplainAboutStringAlloc && scope.callCtx == nil { + stack.err = errFuncCallNotAllowedStrAlloc + return false + } fnv, err := scope.findGlobalInternal(op.FnName) if fnv == nil { if err == nil { - err = fmt.Errorf("function %s not found", op.FnName) + if op.ComplainAboutStringAlloc { + err = errFuncCallNotAllowedStrAlloc + } else { + err = fmt.Errorf("function %s not found", op.FnName) + } } stack.err = err return false diff --git a/pkg/proc/variables_test.go b/pkg/proc/variables_test.go index 277eb5d1..4f4afca6 100644 --- a/pkg/proc/variables_test.go +++ b/pkg/proc/variables_test.go @@ -1284,8 +1284,8 @@ func TestCallFunction(t *testing.T) { {"call1(one+two, 4)", []string{":int:7"}, nil, 0}, {"callpanic()", []string{`~panic:interface {}:interface {}(string) "callpanic panicked"`}, nil, 0}, {`stringsJoin(nil, "")`, []string{`:string:""`}, nil, 0}, - {`stringsJoin(stringslice, comma)`, []string{`:string:"one,two,three"`}, nil, 1}, - {`stringsJoin(stringslice, "~~")`, []string{`:string:"one~~two~~three"`}, nil, 2}, + {`stringsJoin(stringslice, comma)`, []string{`:string:"one,two,three"`}, nil, 0}, + {`stringsJoin(stringslice, "~~")`, []string{`:string:"one~~two~~three"`}, nil, 1}, {`stringsJoin(s1, comma)`, nil, errors.New(`could not find symbol value for s1`), 1}, {`stringsJoin(intslice, comma)`, nil, errors.New("can not convert value of type []int to []string"), 1}, {`noreturncall(2)`, nil, nil, 0}, @@ -1299,15 +1299,15 @@ func TestCallFunction(t *testing.T) { // Call types tests (methods, function pointers, etc.) // The following set of calls was constructed using https://docs.google.com/document/d/1bMwCey-gmqZVTpRax-ESeVuZGmjwbocYs1iHplK-cjo/pub as a reference - {`a.VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil, 1}, // direct call of a method with value receiver / on a value + {`a.VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil, 0}, // direct call of a method with value receiver / on a value - {`a.PRcvr(2)`, []string{`:string:"2 - 3 = -1"`}, nil, 1}, // direct call of a method with pointer receiver / on a value - {`pa.VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil, 1}, // direct call of a method with value receiver / on a pointer - {`pa.PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil, 1}, // direct call of a method with pointer receiver / on a pointer + {`a.PRcvr(2)`, []string{`:string:"2 - 3 = -1"`}, nil, 0}, // direct call of a method with pointer receiver / on a value + {`pa.VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil, 0}, // direct call of a method with value receiver / on a pointer + {`pa.PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil, 0}, // direct call of a method with pointer receiver / on a pointer - {`vable_pa.VRcvr(6)`, []string{`:string:"6 + 6 = 12"`}, nil, 1}, // indirect call of method on interface / containing value with value method - {`pable_pa.PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil, 1}, // indirect call of method on interface / containing pointer with value method - {`vable_a.VRcvr(5)`, []string{`:string:"5 + 3 = 8"`}, nil, 1}, // indirect call of method on interface / containing pointer with pointer method + {`vable_pa.VRcvr(6)`, []string{`:string:"6 + 6 = 12"`}, nil, 0}, // indirect call of method on interface / containing value with value method + {`pable_pa.PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil, 0}, // indirect call of method on interface / containing pointer with value method + {`vable_a.VRcvr(5)`, []string{`:string:"5 + 3 = 8"`}, nil, 0}, // indirect call of method on interface / containing pointer with pointer method {`pa.nonexistent()`, nil, errors.New("pa has no member nonexistent"), 0}, {`a.nonexistent()`, nil, errors.New("a has no member nonexistent"), 0}, @@ -1316,14 +1316,14 @@ func TestCallFunction(t *testing.T) { {`pable_pa.nonexistent()`, nil, errors.New("pable_pa has no member nonexistent"), 0}, {`fn2glob(10, 20)`, []string{":int:30"}, nil, 0}, // indirect call of func value / set to top-level func - {`fn2clos(11)`, []string{`:string:"1 + 6 + 11 = 18"`}, nil, 1}, // indirect call of func value / set to func literal - {`fn2clos(12)`, []string{`:string:"2 + 6 + 12 = 20"`}, nil, 1}, - {`fn2valmeth(13)`, []string{`:string:"13 + 6 = 19"`}, nil, 1}, // indirect call of func value / set to value method - {`fn2ptrmeth(14)`, []string{`:string:"14 - 6 = 8"`}, nil, 1}, // indirect call of func value / set to pointer method + {`fn2clos(11)`, []string{`:string:"1 + 6 + 11 = 18"`}, nil, 0}, // indirect call of func value / set to func literal + {`fn2clos(12)`, []string{`:string:"2 + 6 + 12 = 20"`}, nil, 0}, + {`fn2valmeth(13)`, []string{`:string:"13 + 6 = 19"`}, nil, 0}, // indirect call of func value / set to value method + {`fn2ptrmeth(14)`, []string{`:string:"14 - 6 = 8"`}, nil, 0}, // indirect call of func value / set to pointer method {"fn2nil()", nil, errors.New("nil pointer dereference"), 0}, - {"ga.PRcvr(2)", []string{`:string:"2 - 0 = 2"`}, nil, 1}, + {"ga.PRcvr(2)", []string{`:string:"2 - 0 = 2"`}, nil, 0}, {"x.CallMe()", nil, nil, 0}, {"x2.CallMe(5)", []string{":int:25"}, nil, 0}, @@ -1332,10 +1332,10 @@ func TestCallFunction(t *testing.T) { // Nested function calls tests - {`onetwothree(intcallpanic(2))`, []string{`:[]int:[]int len: 3, cap: 3, [3,4,5]`}, nil, 1}, + {`onetwothree(intcallpanic(2))`, []string{`:[]int:[]int len: 3, cap: 3, [3,4,5]`}, nil, 0}, {`onetwothree(intcallpanic(0))`, []string{`~panic:interface {}:interface {}(string) "panic requested"`}, nil, 0}, - {`onetwothree(intcallpanic(2)+1)`, []string{`:[]int:[]int len: 3, cap: 3, [4,5,6]`}, nil, 1}, - {`onetwothree(intcallpanic("not a number"))`, nil, errors.New("can not convert \"not a number\" constant to int"), 1}, + {`onetwothree(intcallpanic(2)+1)`, []string{`:[]int:[]int len: 3, cap: 3, [4,5,6]`}, nil, 0}, + {`onetwothree(intcallpanic("not a number"))`, nil, altErrors("can not convert \"not a number\" constant to int", "literal string can not be allocated because function calls are not allowed without using 'call'"), 1}, // Variable setting tests {`pa2 = getAStructPtr(8); pa2`, []string{`pa2:*main.astruct:*main.astruct {X: 8}`}, nil, 1}, @@ -1355,14 +1355,14 @@ func TestCallFunction(t *testing.T) { var testcases112 = []testCaseCallFunction{ // string allocation requires trusted argument order, which we don't have in Go 1.11 - {`stringsJoin(stringslice, ",")`, []string{`:string:"one,two,three"`}, nil, 2}, + {`stringsJoin(stringslice, ",")`, []string{`:string:"one,two,three"`}, nil, 1}, {`str = "a new string"; str`, []string{`str:string:"a new string"`}, nil, 1}, // support calling optimized functions {`strings.Join(nil, "")`, []string{`:string:""`}, nil, 0}, - {`strings.Join(stringslice, comma)`, []string{`:string:"one,two,three"`}, nil, 1}, + {`strings.Join(stringslice, comma)`, []string{`:string:"one,two,three"`}, nil, 0}, {`strings.Join(intslice, comma)`, nil, errors.New("can not convert value of type []int to []string"), 1}, - {`strings.Join(stringslice, ",")`, []string{`:string:"one,two,three"`}, nil, 2}, + {`strings.Join(stringslice, ",")`, []string{`:string:"one,two,three"`}, nil, 1}, {`strings.LastIndexByte(stringslice[1], 'w')`, []string{":int:1"}, nil, 0}, {`strings.LastIndexByte(stringslice[1], 'o')`, []string{":int:2"}, nil, 0}, {`d.Base.Method()`, []string{`:int:4`}, nil, 0}, @@ -1374,15 +1374,15 @@ func TestCallFunction(t *testing.T) { // Method calls on a value returned by a function - {`getAStruct(3).VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil, 1}, // direct call of a method with value receiver / on a value + {`getAStruct(3).VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil, 0}, // direct call of a method with value receiver / on a value {`getAStruct(3).PRcvr(2)`, nil, errors.New("could not set call receiver: cannot use getAStruct(3).PRcvr as argument pa in function main.(*astruct).PRcvr: stack object passed to escaping pointer: pa"), 0}, // direct call of a method with pointer receiver / on a value - {`getAStructPtr(6).VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil, 2}, // direct call of a method with value receiver / on a pointer - {`getAStructPtr(6).PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil, 2}, // direct call of a method with pointer receiver / on a pointer + {`getAStructPtr(6).VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil, 1}, // direct call of a method with value receiver / on a pointer + {`getAStructPtr(6).PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil, 1}, // direct call of a method with pointer receiver / on a pointer - {`getVRcvrableFromAStruct(3).VRcvr(6)`, []string{`:string:"6 + 3 = 9"`}, nil, 3}, // indirect call of method on interface / containing value with value method - {`getPRcvrableFromAStructPtr(6).PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil, 3}, // indirect call of method on interface / containing pointer with value method - {`getVRcvrableFromAStructPtr(6).VRcvr(5)`, []string{`:string:"5 + 6 = 11"`}, nil, 3}, // indirect call of method on interface / containing pointer with pointer method + {`getVRcvrableFromAStruct(3).VRcvr(6)`, []string{`:string:"6 + 3 = 9"`}, nil, 2}, // indirect call of method on interface / containing value with value method + {`getPRcvrableFromAStructPtr(6).PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil, 2}, // indirect call of method on interface / containing pointer with value method + {`getVRcvrableFromAStructPtr(6).VRcvr(5)`, []string{`:string:"5 + 6 = 11"`}, nil, 2}, // indirect call of method on interface / containing pointer with pointer method } var testcasesBefore114After112 = []testCaseCallFunction{ @@ -1394,11 +1394,11 @@ func TestCallFunction(t *testing.T) { } var testcases117 = []testCaseCallFunction{ - {`regabistacktest("one", "two", "three", "four", "five", 4)`, []string{`:string:"onetwo"`, `:string:"twothree"`, `:string:"threefour"`, `:string:"fourfive"`, `:string:"fiveone"`, ":uint8:8"}, nil, 10}, + {`regabistacktest("one", "two", "three", "four", "five", 4)`, []string{`:string:"onetwo"`, `:string:"twothree"`, `:string:"threefour"`, `:string:"fourfive"`, `:string:"fiveone"`, ":uint8:8"}, nil, 5}, {`regabistacktest2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)`, []string{":int:3", ":int:5", ":int:7", ":int:9", ":int:11", ":int:13", ":int:15", ":int:17", ":int:19", ":int:11"}, nil, 0}, - {`issue2698.String()`, []string{`:string:"1 2 3 4"`}, nil, 1}, - {`issue3364.String()`, []string{`:string:"1 2"`}, nil, 1}, - {`regabistacktest3(rast3, 5)`, []string{`:[10]string:[10]string ["onetwo","twothree","threefour","fourfive","fivesix","sixseven","sevenheight","heightnine","nineten","tenone"]`, ":uint8:15"}, nil, 10}, + {`issue2698.String()`, []string{`:string:"1 2 3 4"`}, nil, 0}, + {`issue3364.String()`, []string{`:string:"1 2"`}, nil, 0}, + {`regabistacktest3(rast3, 5)`, []string{`:[10]string:[10]string ["onetwo","twothree","threefour","fourfive","fivesix","sixseven","sevenheight","heightnine","nineten","tenone"]`, ":uint8:15"}, nil, 0}, {`floatsum(1, 2)`, []string{":float64:3"}, nil, 0}, } @@ -1486,8 +1486,22 @@ func testCallFunctionIntl(t *testing.T, grp *proc.TargetGroup, p *proc.Target, t if err == nil { t.Fatalf("call %q: expected error %q, got no error", tc.expr, tc.err.Error()) } - if tc.err.Error() != err.Error() { - t.Fatalf("call %q: expected error %q, got %q", tc.expr, tc.err.Error(), err.Error()) + switch e := tc.err.(type) { + case *altError: + ok := false + for _, tgtErr := range e.errs { + if tgtErr == err.Error() { + ok = true + break + } + } + if !ok { + t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error()) + } + default: + if tc.err.Error() != err.Error() { + t.Fatalf("call %q: expected error %q, got %q", tc.expr, tc.err.Error(), err.Error()) + } } return } diff --git a/service/dap/server_test.go b/service/dap/server_test.go index c99e0766..079fa888 100644 --- a/service/dap/server_test.go +++ b/service/dap/server_test.go @@ -6157,7 +6157,7 @@ func TestSetVariable(t *testing.T) { tester.expectSetVariable(barRef, "Baz", "42") tester.evaluate("bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren) - tester.failSetVariable(barRef, "Baz", `"string"`, "can not convert") + tester.failSetVariable(barRef, "Baz", `"string"`, "can not") // int checkVarExact(t, locals, -1, "a2", "a2", "6", "int", noChildren)