proc: initial support for expressions with range-over-func (#3750)
Supports viewing local variables and evaluating expressions correctly when range-over-func is used. The same limitations that the previous commit on this line had still apply (no inlining, wrong way to identify the range parent in some cases). Updates #3733
This commit is contained in:
parent
0d0d2e1b16
commit
ed2960b01c
@ -960,7 +960,7 @@ func (rbpi *returnBreakpointInfo) Collect(t *Target, thread Thread) []*Variable
|
|||||||
return returnInfoError("could not read function entry", err, thread.ProcessMemory())
|
return returnInfoError("could not read function entry", err, thread.ProcessMemory())
|
||||||
}
|
}
|
||||||
|
|
||||||
vars, err := scope.Locals(0)
|
vars, err := scope.Locals(0, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return returnInfoError("could not evaluate return variables", err, thread.ProcessMemory())
|
return returnInfoError("could not evaluate return variables", err, thread.ProcessMemory())
|
||||||
}
|
}
|
||||||
|
122
pkg/proc/eval.go
122
pkg/proc/eval.go
@ -25,7 +25,10 @@ import (
|
|||||||
|
|
||||||
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
|
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
|
||||||
|
|
||||||
const goDictionaryName = ".dict"
|
const (
|
||||||
|
goDictionaryName = ".dict"
|
||||||
|
goClosurePtr = ".closureptr"
|
||||||
|
)
|
||||||
|
|
||||||
// EvalScope is the scope for variable evaluation. Contains the thread,
|
// EvalScope is the scope for variable evaluation. Contains the thread,
|
||||||
// current location (PC), and canonical frame address.
|
// current location (PC), and canonical frame address.
|
||||||
@ -48,6 +51,9 @@ type EvalScope struct {
|
|||||||
callCtx *callContext
|
callCtx *callContext
|
||||||
|
|
||||||
dictAddr uint64 // dictionary address for instantiated generic functions
|
dictAddr uint64 // dictionary address for instantiated generic functions
|
||||||
|
|
||||||
|
enclosingRangeScopes []*EvalScope
|
||||||
|
rangeFrames []Stackframe
|
||||||
}
|
}
|
||||||
|
|
||||||
type localsFlags uint8
|
type localsFlags uint8
|
||||||
@ -290,8 +296,89 @@ func (scope *EvalScope) ChanGoroutines(expr string, start, count int) ([]int64,
|
|||||||
return goids, nil
|
return goids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locals returns all variables in 'scope'.
|
// Locals returns all variables in 'scope' named wantedName, or all of them
|
||||||
func (scope *EvalScope) Locals(flags localsFlags) ([]*Variable, error) {
|
// if wantedName is "".
|
||||||
|
// If scope is the scope for a range-over-func closure body it will merge in
|
||||||
|
// the scopes of the enclosing functions.
|
||||||
|
func (scope *EvalScope) Locals(flags localsFlags, wantedName string) ([]*Variable, error) {
|
||||||
|
var scopes [][]*Variable
|
||||||
|
filter := func(vars []*Variable) []*Variable {
|
||||||
|
if wantedName == "" || vars == nil {
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
vars2 := []*Variable{}
|
||||||
|
for _, v := range vars {
|
||||||
|
if v.Name == wantedName {
|
||||||
|
vars2 = append(vars2, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vars2
|
||||||
|
}
|
||||||
|
|
||||||
|
vars0, err := scope.simpleLocals(flags, wantedName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vars0 = filter(vars0)
|
||||||
|
if scope.Fn.extra(scope.BinInfo).rangeParent == nil || scope.target == nil || scope.g == nil {
|
||||||
|
return vars0, nil
|
||||||
|
}
|
||||||
|
if wantedName != "" && len(vars0) > 0 {
|
||||||
|
return vars0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes = append(scopes, vars0)
|
||||||
|
|
||||||
|
if scope.rangeFrames == nil {
|
||||||
|
scope.rangeFrames, err = rangeFuncStackTrace(scope.target, scope.g)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
scope.rangeFrames = scope.rangeFrames[1:]
|
||||||
|
scope.enclosingRangeScopes = make([]*EvalScope, len(scope.rangeFrames))
|
||||||
|
}
|
||||||
|
for i, scope2 := range scope.enclosingRangeScopes {
|
||||||
|
if i == len(scope.enclosingRangeScopes)-1 {
|
||||||
|
// Last one is the caller frame, we shouldn't check it
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if scope2 == nil {
|
||||||
|
scope2 = FrameToScope(scope.target, scope.target.Memory(), scope.g, scope.threadID, scope.rangeFrames[i:]...)
|
||||||
|
scope.enclosingRangeScopes[i] = scope2
|
||||||
|
}
|
||||||
|
vars, err := scope2.simpleLocals(flags, wantedName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vars = filter(vars)
|
||||||
|
scopes = append(scopes, vars)
|
||||||
|
if wantedName != "" && len(vars) > 0 {
|
||||||
|
return vars, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := []*Variable{}
|
||||||
|
for i := len(scopes) - 1; i >= 0; i-- {
|
||||||
|
vars = append(vars, scopes[i]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply shadowning
|
||||||
|
lvn := map[string]*Variable{}
|
||||||
|
for _, v := range vars {
|
||||||
|
if otherv := lvn[v.Name]; otherv != nil {
|
||||||
|
otherv.Flags |= VariableShadowed
|
||||||
|
}
|
||||||
|
lvn[v.Name] = v
|
||||||
|
}
|
||||||
|
return vars, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// simpleLocals returns all local variables in 'scope'.
|
||||||
|
// This function does not try to merge the scopes of range-over-func closure
|
||||||
|
// bodies with their enclosing function, for that use (*EvalScope).Locals or
|
||||||
|
// (*EvalScope).FindLocal instead.
|
||||||
|
// If wantedName is specified only variables called wantedName or "&"+wantedName are returned.
|
||||||
|
func (scope *EvalScope) simpleLocals(flags localsFlags, wantedName string) ([]*Variable, error) {
|
||||||
if scope.Fn == nil {
|
if scope.Fn == nil {
|
||||||
return nil, errors.New("unable to find function context")
|
return nil, errors.New("unable to find function context")
|
||||||
}
|
}
|
||||||
@ -341,8 +428,25 @@ func (scope *EvalScope) Locals(flags localsFlags) ([]*Variable, error) {
|
|||||||
vars := make([]*Variable, 0, len(varEntries))
|
vars := make([]*Variable, 0, len(varEntries))
|
||||||
depths := make([]int, 0, len(varEntries))
|
depths := make([]int, 0, len(varEntries))
|
||||||
for _, entry := range varEntries {
|
for _, entry := range varEntries {
|
||||||
if name, _ := entry.Val(dwarf.AttrName).(string); name == goDictionaryName {
|
name, _ := entry.Val(dwarf.AttrName).(string)
|
||||||
continue
|
switch {
|
||||||
|
case wantedName != "":
|
||||||
|
if name != wantedName && name != "&"+wantedName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if name == goDictionaryName || name == goClosurePtr || strings.HasPrefix(name, "#state") || strings.HasPrefix(name, "&#state") || strings.HasPrefix(name, "#next") || strings.HasPrefix(name, "&#next") || strings.HasPrefix(name, "#yield") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scope.Fn.rangeParentName() != "" {
|
||||||
|
// Skip return values and closure variables for range-over-func closure bodies
|
||||||
|
if strings.HasPrefix(name, "~") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry.Val(godwarf.AttrGoClosureOffset) != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val, err := extractVarInfoFromEntry(scope.target, scope.BinInfo, scope.image(), scope.Regs, scope.Mem, entry.Tree, scope.dictAddr)
|
val, err := extractVarInfoFromEntry(scope.target, scope.BinInfo, scope.image(), scope.Regs, scope.Mem, entry.Tree, scope.dictAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -519,7 +623,7 @@ func (scope *EvalScope) SetVariable(name, value string) error {
|
|||||||
|
|
||||||
// LocalVariables returns all local variables from the current function scope.
|
// LocalVariables returns all local variables from the current function scope.
|
||||||
func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
|
func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
|
||||||
vars, err := scope.Locals(0)
|
vars, err := scope.Locals(0, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -533,7 +637,7 @@ func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
|
|||||||
|
|
||||||
// FunctionArguments returns the name, value, and type of all current function arguments.
|
// FunctionArguments returns the name, value, and type of all current function arguments.
|
||||||
func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
|
func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
|
||||||
vars, err := scope.Locals(0)
|
vars, err := scope.Locals(0, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1042,9 +1146,9 @@ func (stack *evalStack) pushLocal(scope *EvalScope, name string, frame int64) (f
|
|||||||
stack.err = err2
|
stack.err = err2
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vars, err = frameScope.Locals(0)
|
vars, err = frameScope.Locals(0, name)
|
||||||
} else {
|
} else {
|
||||||
vars, err = scope.Locals(0)
|
vars, err = scope.Locals(0, name)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stack.err = err
|
stack.err = err
|
||||||
|
@ -855,7 +855,7 @@ func funcCallStep(callScope *EvalScope, stack *evalStack, thread Thread) bool {
|
|||||||
flags |= localsTrustArgOrder
|
flags |= localsTrustArgOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
fncall.retvars, err = retScope.Locals(flags)
|
fncall.retvars, err = retScope.Locals(flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fncall.err = fmt.Errorf("could not get return values: %v", err)
|
fncall.err = fmt.Errorf("could not get return values: %v", err)
|
||||||
break
|
break
|
||||||
|
@ -3257,7 +3257,7 @@ func TestDebugStripped(t *testing.T) {
|
|||||||
// return an error instead of panic.
|
// return an error instead of panic.
|
||||||
s, err := proc.ThreadScope(p, p.CurrentThread())
|
s, err := proc.ThreadScope(p, p.CurrentThread())
|
||||||
assertNoError(err, t, "ThreadScope")
|
assertNoError(err, t, "ThreadScope")
|
||||||
_, err = s.Locals(0)
|
_, err = s.Locals(0, "")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected an error to be returned from scope.Locals in stripped binary")
|
t.Error("expected an error to be returned from scope.Locals in stripped binary")
|
||||||
}
|
}
|
||||||
@ -3645,7 +3645,7 @@ func testDeclLineCount(t *testing.T, p *proc.Target, lineno int, tgtvars []strin
|
|||||||
assertLineNumber(p, t, lineno, "Program did not continue to correct next location")
|
assertLineNumber(p, t, lineno, "Program did not continue to correct next location")
|
||||||
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
||||||
assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno))
|
assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno))
|
||||||
vars, err := scope.Locals(0)
|
vars, err := scope.Locals(0, "")
|
||||||
assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno))
|
assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno))
|
||||||
if len(vars) != len(tgtvars) {
|
if len(vars) != len(tgtvars) {
|
||||||
t.Fatalf("wrong number of variables %d (:%d)", len(vars), lineno)
|
t.Fatalf("wrong number of variables %d (:%d)", len(vars), lineno)
|
||||||
@ -6310,6 +6310,60 @@ func TestRangeOverFuncNext(t *testing.T) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertLocals := func(t *testing.T, varnames ...string) seqTest {
|
||||||
|
return seqTest{
|
||||||
|
contNothing,
|
||||||
|
func(p *proc.Target) {
|
||||||
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
||||||
|
assertNoError(err, t, "GoroutineScope")
|
||||||
|
vars, err := scope.Locals(0, "")
|
||||||
|
assertNoError(err, t, "Locals")
|
||||||
|
|
||||||
|
gotnames := make([]string, len(vars))
|
||||||
|
for i := range vars {
|
||||||
|
gotnames[i] = vars[i].Name
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := true
|
||||||
|
if len(vars) != len(varnames) {
|
||||||
|
ok = false
|
||||||
|
} else {
|
||||||
|
for i := range vars {
|
||||||
|
if vars[i].Name != varnames[i] {
|
||||||
|
ok = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Wrong variable names, expected %q, got %q", varnames, gotnames)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEval := func(t *testing.T, exprvals ...string) seqTest {
|
||||||
|
return seqTest{
|
||||||
|
contNothing,
|
||||||
|
func(p *proc.Target) {
|
||||||
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
||||||
|
assertNoError(err, t, "GoroutineScope")
|
||||||
|
for i := 0; i < len(exprvals); i += 2 {
|
||||||
|
expr, tgt := exprvals[i], exprvals[i+1]
|
||||||
|
v, err := scope.EvalExpression(expr, normalLoadConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not evaluate %q: %v", expr, err)
|
||||||
|
} else {
|
||||||
|
out := api.ConvertVar(v).SinglelineString()
|
||||||
|
if out != tgt {
|
||||||
|
t.Errorf("Wrong value for %q, got %q expected %q", expr, out, tgt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
withTestProcessArgs("rangeoverfunc", t, ".", []string{}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
withTestProcessArgs("rangeoverfunc", t, ".", []string{}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||||
|
|
||||||
t.Run("TestTrickyIterAll1", func(t *testing.T) {
|
t.Run("TestTrickyIterAll1", func(t *testing.T) {
|
||||||
@ -6319,13 +6373,22 @@ func TestRangeOverFuncNext(t *testing.T) {
|
|||||||
nx(25),
|
nx(25),
|
||||||
nx(26),
|
nx(26),
|
||||||
nx(27), // for _, x := range ...
|
nx(27), // for _, x := range ...
|
||||||
|
assertLocals(t, "trickItAll", "i"),
|
||||||
|
assertEval(t, "i", "0"),
|
||||||
nx(27), // for _, x := range ... (TODO: this probably shouldn't be here but it's also very hard to skip stopping here a second time)
|
nx(27), // for _, x := range ... (TODO: this probably shouldn't be here but it's also very hard to skip stopping here a second time)
|
||||||
nx(28), // i += x
|
nx(28), // i += x
|
||||||
|
assertLocals(t, "trickItAll", "i", "x"),
|
||||||
|
assertEval(t,
|
||||||
|
"i", "0",
|
||||||
|
"x", "30"),
|
||||||
nx(29), // if i >= 36 {
|
nx(29), // if i >= 36 {
|
||||||
nx(32),
|
nx(32),
|
||||||
nx(27), // for _, x := range ...
|
nx(27), // for _, x := range ...
|
||||||
notAtEntryPoint(t),
|
notAtEntryPoint(t),
|
||||||
nx(28), // i += x
|
nx(28), // i += x
|
||||||
|
assertEval(t,
|
||||||
|
"i", "30",
|
||||||
|
"x", "7"),
|
||||||
nx(29), // if i >= 36 {
|
nx(29), // if i >= 36 {
|
||||||
nx(30), // break
|
nx(30), // break
|
||||||
nx(32),
|
nx(32),
|
||||||
@ -6360,26 +6423,45 @@ func TestRangeOverFuncNext(t *testing.T) {
|
|||||||
nx(48), // for _, x := range... (x == -1)
|
nx(48), // for _, x := range... (x == -1)
|
||||||
nx(48),
|
nx(48),
|
||||||
nx(49), // if x == -4
|
nx(49), // if x == -4
|
||||||
|
assertLocals(t, "result", "x"),
|
||||||
|
assertEval(t,
|
||||||
|
"result", "[]int len: 0, cap: 0, nil",
|
||||||
|
"x", "-1"),
|
||||||
|
|
||||||
nx(52), // for _, y := range... (y == 1)
|
nx(52), // for _, y := range... (y == 1)
|
||||||
nx(52),
|
nx(52),
|
||||||
nx(53), // if y == 3
|
nx(53), // if y == 3
|
||||||
|
assertLocals(t, "result", "x", "y"),
|
||||||
|
assertEval(t,
|
||||||
|
"result", "[]int len: 0, cap: 0, nil",
|
||||||
|
"x", "-1",
|
||||||
|
"y", "1"),
|
||||||
nx(56), // result = append(result, y)
|
nx(56), // result = append(result, y)
|
||||||
nx(57),
|
nx(57),
|
||||||
nx(52), // for _, y := range... (y == 2)
|
nx(52), // for _, y := range... (y == 2)
|
||||||
notAtEntryPoint(t),
|
notAtEntryPoint(t),
|
||||||
nx(53), // if y == 3
|
nx(53), // if y == 3
|
||||||
|
assertEval(t,
|
||||||
|
"x", "-1",
|
||||||
|
"y", "2"),
|
||||||
nx(56), // result = append(result, y)
|
nx(56), // result = append(result, y)
|
||||||
nx(57),
|
nx(57),
|
||||||
nx(52), // for _, y := range... (y == 3)
|
nx(52), // for _, y := range... (y == 3)
|
||||||
nx(53), // if y == 3
|
nx(53), // if y == 3
|
||||||
|
assertEval(t,
|
||||||
|
"x", "-1",
|
||||||
|
"y", "3"),
|
||||||
nx(54), // break
|
nx(54), // break
|
||||||
nx(57),
|
nx(57),
|
||||||
nx(58), // result = append(result, x)
|
nx(58), // result = append(result, x)
|
||||||
nx(59),
|
nx(59),
|
||||||
|
|
||||||
nx(48), // for _, x := range... (x == -2)
|
nx(48), // for _, x := range... (x == -2)
|
||||||
|
notAtEntryPoint(t),
|
||||||
nx(49), // if x == -4
|
nx(49), // if x == -4
|
||||||
|
assertEval(t,
|
||||||
|
"result", "[]int len: 3, cap: 4, [1,2,-1]",
|
||||||
|
"x", "-2"),
|
||||||
nx(52), // for _, y := range... (y == 1)
|
nx(52), // for _, y := range... (y == 1)
|
||||||
nx(52),
|
nx(52),
|
||||||
nx(53), // if y == 3
|
nx(53), // if y == 3
|
||||||
@ -6398,6 +6480,9 @@ func TestRangeOverFuncNext(t *testing.T) {
|
|||||||
nx(59),
|
nx(59),
|
||||||
|
|
||||||
nx(48), // for _, x := range... (x == -4)
|
nx(48), // for _, x := range... (x == -4)
|
||||||
|
assertEval(t,
|
||||||
|
"result", "[]int len: 6, cap: 8, [1,2,-1,1,2,-2]",
|
||||||
|
"x", "-4"),
|
||||||
nx(49), // if x == -4
|
nx(49), // if x == -4
|
||||||
nx(50), // break
|
nx(50), // break
|
||||||
nx(59),
|
nx(59),
|
||||||
@ -6479,31 +6564,51 @@ func TestRangeOverFuncNext(t *testing.T) {
|
|||||||
nx(85), // for _, w := range (w == 1000)
|
nx(85), // for _, w := range (w == 1000)
|
||||||
nx(85),
|
nx(85),
|
||||||
nx(86), // result = append(result, w)
|
nx(86), // result = append(result, w)
|
||||||
|
assertEval(t,
|
||||||
|
"w", "1000",
|
||||||
|
"result", "[]int len: 0, cap: 0, nil"),
|
||||||
nx(87), // if w == 2000
|
nx(87), // if w == 2000
|
||||||
|
assertLocals(t, "result", "w"),
|
||||||
|
assertEval(t, "result", "[]int len: 1, cap: 1, [1000]"),
|
||||||
nx(90), // for _, x := range (x == 100)
|
nx(90), // for _, x := range (x == 100)
|
||||||
nx(90),
|
nx(90),
|
||||||
nx(91), // for _, y := range (y == 10)
|
nx(91), // for _, y := range (y == 10)
|
||||||
nx(91),
|
nx(91),
|
||||||
nx(92), // result = append(result, y)
|
nx(92), // result = append(result, y)
|
||||||
|
assertLocals(t, "result", "w", "x", "y"),
|
||||||
|
assertEval(t,
|
||||||
|
"w", "1000",
|
||||||
|
"x", "100",
|
||||||
|
"y", "10"),
|
||||||
|
|
||||||
nx(93), // for _, z := range (z == 1)
|
nx(93), // for _, z := range (z == 1)
|
||||||
nx(93),
|
nx(93),
|
||||||
nx(94), // if z&1 == 1
|
nx(94), // if z&1 == 1
|
||||||
|
assertLocals(t, "result", "w", "x", "y", "z"),
|
||||||
|
assertEval(t,
|
||||||
|
"w", "1000",
|
||||||
|
"x", "100",
|
||||||
|
"y", "10",
|
||||||
|
"z", "1"),
|
||||||
nx(95), // continue
|
nx(95), // continue
|
||||||
|
|
||||||
nx(93), // for _, z := range (z == 2)
|
nx(93), // for _, z := range (z == 2)
|
||||||
nx(94), // if z&1 == 1
|
nx(94), // if z&1 == 1
|
||||||
|
assertEval(t, "z", "2"),
|
||||||
nx(97), // result = append(result, z)
|
nx(97), // result = append(result, z)
|
||||||
nx(98), // if z >= 4 {
|
nx(98), // if z >= 4 {
|
||||||
nx(101),
|
nx(101),
|
||||||
|
|
||||||
nx(93), // for _, z := range (z == 3)
|
nx(93), // for _, z := range (z == 3)
|
||||||
nx(94), // if z&1 == 1
|
nx(94), // if z&1 == 1
|
||||||
|
assertEval(t, "z", "3"),
|
||||||
nx(95), // continue
|
nx(95), // continue
|
||||||
|
|
||||||
nx(93), // for _, z := range (z == 4)
|
nx(93), // for _, z := range (z == 4)
|
||||||
nx(94), // if z&1 == 1
|
nx(94), // if z&1 == 1
|
||||||
|
assertEval(t, "z", "4"),
|
||||||
nx(97), // result = append(result, z)
|
nx(97), // result = append(result, z)
|
||||||
|
assertEval(t, "result", "[]int len: 3, cap: 4, [1000,10,2]"),
|
||||||
nx(98), // if z >= 4 {
|
nx(98), // if z >= 4 {
|
||||||
nx(99), // continue W
|
nx(99), // continue W
|
||||||
nx(101),
|
nx(101),
|
||||||
@ -6513,6 +6618,9 @@ func TestRangeOverFuncNext(t *testing.T) {
|
|||||||
nx(85), // for _, w := range (w == 2000)
|
nx(85), // for _, w := range (w == 2000)
|
||||||
nx(86), // result = append(result, w)
|
nx(86), // result = append(result, w)
|
||||||
nx(87), // if w == 2000
|
nx(87), // if w == 2000
|
||||||
|
assertEval(t,
|
||||||
|
"w", "2000",
|
||||||
|
"result", "[]int len: 5, cap: 8, [1000,10,2,4,2000]"),
|
||||||
nx(88), // break
|
nx(88), // break
|
||||||
nx(106),
|
nx(106),
|
||||||
nx(107), // fmt.Println
|
nx(107), // fmt.Println
|
||||||
|
@ -18,7 +18,7 @@ func (it *stackIterator) readSigtrampgoContext() (*op.DwarfRegisters, error) {
|
|||||||
bi := it.bi
|
bi := it.bi
|
||||||
|
|
||||||
findvar := func(name string) *Variable {
|
findvar := func(name string) *Variable {
|
||||||
vars, _ := scope.Locals(0)
|
vars, _ := scope.Locals(0, name)
|
||||||
for i := range vars {
|
for i := range vars {
|
||||||
if vars[i].Name == name {
|
if vars[i].Name == name {
|
||||||
return vars[i]
|
return vars[i]
|
||||||
|
Loading…
Reference in New Issue
Block a user