mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 04:35:19 +08:00
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.
This commit is contained in:
committed by
GitHub
parent
0b821e4516
commit
2685a42bc0
@ -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: // <expression>.<identifier>
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user