github.com/traefik/yaegi@v0.15.1/interp/debugger.go (about)

     1  package interp
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"go/token"
     8  	"reflect"
     9  	"sort"
    10  	"sync"
    11  )
    12  
    13  var (
    14  	// ErrNotLive indicates that the specified ID does not refer to a (live) Go
    15  	// routine.
    16  	ErrNotLive = errors.New("not live")
    17  
    18  	// ErrRunning indicates that the specified Go routine is running.
    19  	ErrRunning = errors.New("running")
    20  
    21  	// ErrNotRunning indicates that the specified Go routine is running.
    22  	ErrNotRunning = errors.New("not running")
    23  )
    24  
    25  var rNodeType = reflect.TypeOf((*node)(nil)).Elem()
    26  
    27  // A Debugger can be used to debug a Yaegi program.
    28  type Debugger struct {
    29  	interp  *Interpreter
    30  	events  func(*DebugEvent)
    31  	context context.Context
    32  	cancel  context.CancelFunc
    33  
    34  	gWait *sync.WaitGroup
    35  	gLock *sync.Mutex
    36  	gID   int
    37  	gLive map[int]*debugRoutine
    38  
    39  	result reflect.Value
    40  	err    error
    41  }
    42  
    43  // go routine debug state.
    44  type debugRoutine struct {
    45  	id int
    46  
    47  	mode    DebugEventReason
    48  	running bool
    49  	resume  chan struct{}
    50  
    51  	fDepth int
    52  	fStep  int
    53  }
    54  
    55  // node debug state.
    56  type nodeDebugData struct {
    57  	program     *Program
    58  	breakOnLine bool
    59  	breakOnCall bool
    60  }
    61  
    62  // frame debug state.
    63  type frameDebugData struct {
    64  	g     *debugRoutine
    65  	node  *node
    66  	name  string
    67  	kind  frameKind
    68  	scope *scope
    69  }
    70  
    71  // frame kind.
    72  type frameKind int
    73  
    74  const (
    75  	// interpreter root frame.
    76  	frameRoot frameKind = iota + 1
    77  
    78  	// function call frame.
    79  	frameCall
    80  
    81  	// closure capture frame.
    82  	frameClosure
    83  )
    84  
    85  // DebugOptions are the debugger options.
    86  type DebugOptions struct {
    87  	// If true, Go routine IDs start at 1 instead of 0.
    88  	GoRoutineStartAt1 bool
    89  }
    90  
    91  // A DebugEvent is an event generated by a debugger.
    92  type DebugEvent struct {
    93  	debugger *Debugger
    94  	reason   DebugEventReason
    95  	frame    *frame
    96  }
    97  
    98  // DebugFrame provides access to stack frame information while debugging a
    99  // program.
   100  type DebugFrame struct {
   101  	event  *DebugEvent
   102  	frames []*frame
   103  }
   104  
   105  // DebugFrameScope provides access to scoped variables while debugging a
   106  // program.
   107  type DebugFrameScope struct {
   108  	frame *frame
   109  }
   110  
   111  // DebugVariable is the name and value of a variable from a debug session.
   112  type DebugVariable struct {
   113  	Name  string
   114  	Value reflect.Value
   115  }
   116  
   117  // DebugGoRoutine provides access to information about a Go routine while
   118  // debugging a program.
   119  type DebugGoRoutine struct {
   120  	id int
   121  }
   122  
   123  // Breakpoint is the result of attempting to set a breakpoint.
   124  type Breakpoint struct {
   125  	// Valid indicates whether the breakpoint was successfully set.
   126  	Valid bool
   127  
   128  	// Position indicates the source position of the breakpoint.
   129  	Position token.Position
   130  }
   131  
   132  // DebugEventReason is the reason a debug event occurred.
   133  type DebugEventReason int
   134  
   135  const (
   136  	// continue execution normally.
   137  	debugRun DebugEventReason = iota
   138  
   139  	// DebugPause is emitted when a pause request is completed. Can be used with
   140  	// Interrupt to request a pause.
   141  	DebugPause
   142  
   143  	// DebugBreak is emitted when a debug target hits a breakpoint.
   144  	DebugBreak
   145  
   146  	// DebugEntry is emitted when a debug target starts executing. Can be used
   147  	// with Step to produce a corresponding event when execution starts.
   148  	DebugEntry
   149  
   150  	// DebugStepInto is emitted when a stepInto request is completed. Can be
   151  	// used with Step or Interrupt to request a stepInto.
   152  	DebugStepInto
   153  
   154  	// DebugStepOver is emitted when a stepOver request is completed. Can be
   155  	// used with Step or Interrupt to request a stepOver.
   156  	DebugStepOver
   157  
   158  	// DebugStepOut is emitted when a stepOut request is completed. Can be used
   159  	// with Step or Interrupt to request a stepOut.
   160  	DebugStepOut
   161  
   162  	// DebugTerminate is emitted when a debug target terminates. Can be used
   163  	// with Interrupt to attempt to terminate the program.
   164  	DebugTerminate
   165  
   166  	// DebugEnterGoRoutine is emitted when a Go routine is entered.
   167  	DebugEnterGoRoutine
   168  
   169  	// DebugExitGoRoutine is emitted when a Go routine is exited.
   170  	DebugExitGoRoutine
   171  )
   172  
   173  // Debug initializes a debugger for the given program.
   174  //
   175  // The program will not start running until Step or Continue has been called. If
   176  // Step is called with DebugEntry, an entry event will be generated before the
   177  // first statement is executed. Otherwise, the debugger will behave as usual.
   178  func (interp *Interpreter) Debug(ctx context.Context, prog *Program, events func(*DebugEvent), opts *DebugOptions) *Debugger {
   179  	dbg := new(Debugger)
   180  	dbg.interp = interp
   181  	dbg.events = events
   182  	dbg.context, dbg.cancel = context.WithCancel(ctx)
   183  	dbg.gWait = new(sync.WaitGroup)
   184  	dbg.gLock = new(sync.Mutex)
   185  	dbg.gLive = make(map[int]*debugRoutine, 1)
   186  
   187  	if opts == nil {
   188  		opts = new(DebugOptions)
   189  	}
   190  	if opts.GoRoutineStartAt1 {
   191  		dbg.gID = 1
   192  	}
   193  
   194  	mainG := dbg.enterGoRoutine()
   195  	mainG.mode = DebugEntry
   196  
   197  	interp.debugger = dbg
   198  	interp.frame.debug = &frameDebugData{kind: frameRoot, g: mainG}
   199  
   200  	prog.root.Walk(func(n *node) bool {
   201  		n.setProgram(prog)
   202  		return true
   203  	}, nil)
   204  
   205  	go func() {
   206  		defer func() { interp.debugger = nil }()
   207  		defer events(&DebugEvent{reason: DebugTerminate})
   208  		defer dbg.cancel()
   209  
   210  		<-mainG.resume
   211  		dbg.events(&DebugEvent{dbg, DebugEnterGoRoutine, interp.frame})
   212  		dbg.result, dbg.err = interp.ExecuteWithContext(ctx, prog)
   213  		dbg.exitGoRoutine(mainG)
   214  		dbg.events(&DebugEvent{dbg, DebugExitGoRoutine, interp.frame})
   215  		dbg.gWait.Wait()
   216  	}()
   217  
   218  	return dbg
   219  }
   220  
   221  // Wait blocks until all Go routines launched by the program have terminated.
   222  // Wait returns the results of `(*Interpreter).Execute`.
   223  func (dbg *Debugger) Wait() (reflect.Value, error) {
   224  	<-dbg.context.Done()
   225  	return dbg.result, dbg.err
   226  }
   227  
   228  // mark entry into a go routine.
   229  func (dbg *Debugger) enterGoRoutine() *debugRoutine {
   230  	g := new(debugRoutine)
   231  	g.resume = make(chan struct{})
   232  
   233  	dbg.gWait.Add(1)
   234  
   235  	dbg.gLock.Lock()
   236  	g.id = dbg.gID
   237  	dbg.gID++
   238  	dbg.gLive[g.id] = g
   239  	dbg.gLock.Unlock()
   240  
   241  	return g
   242  }
   243  
   244  // mark exit from a go routine.
   245  func (dbg *Debugger) exitGoRoutine(g *debugRoutine) {
   246  	dbg.gLock.Lock()
   247  	delete(dbg.gLive, g.id)
   248  	dbg.gLock.Unlock()
   249  
   250  	dbg.gWait.Done()
   251  }
   252  
   253  // get the state for a given go routine, if it's live.
   254  func (dbg *Debugger) getGoRoutine(id int) (*debugRoutine, bool) {
   255  	dbg.gLock.Lock()
   256  	g, ok := dbg.gLive[id]
   257  	dbg.gLock.Unlock()
   258  	return g, ok
   259  }
   260  
   261  // mark entry into a function call.
   262  func (dbg *Debugger) enterCall(nFunc, nCall *node, f *frame) {
   263  	if f.debug != nil {
   264  		f.debug.g.fDepth++
   265  		return
   266  	}
   267  
   268  	f.debug = new(frameDebugData)
   269  	f.debug.g = f.anc.debug.g
   270  	f.debug.scope = nFunc.scope
   271  
   272  	switch nFunc.kind {
   273  	case funcLit:
   274  		f.debug.kind = frameCall
   275  		if nFunc.frame != nil {
   276  			nFunc.frame.debug.kind = frameClosure
   277  			nFunc.frame.debug.node = nFunc
   278  		}
   279  
   280  	case funcDecl:
   281  		f.debug.kind = frameCall
   282  		f.debug.name = nFunc.child[1].ident
   283  	}
   284  
   285  	if nCall != nil && nCall.anc.kind == goStmt {
   286  		f.debug.g = dbg.enterGoRoutine()
   287  		dbg.events(&DebugEvent{dbg, DebugEnterGoRoutine, f})
   288  	}
   289  
   290  	f.debug.g.fDepth++
   291  }
   292  
   293  // mark exit from a function call.
   294  func (dbg *Debugger) exitCall(nFunc, nCall *node, f *frame) {
   295  	_ = nFunc // ignore unused, so exitCall can have the same signature as enterCall
   296  
   297  	f.debug.g.fDepth--
   298  
   299  	if nCall != nil && nCall.anc.kind == goStmt {
   300  		dbg.exitGoRoutine(f.debug.g)
   301  		dbg.events(&DebugEvent{dbg, DebugExitGoRoutine, f})
   302  	}
   303  }
   304  
   305  // called by the interpreter prior to executing the node.
   306  func (dbg *Debugger) exec(n *node, f *frame) (stop bool) {
   307  	f.debug.node = n
   308  
   309  	if n != nil && n.pos == token.NoPos {
   310  		return false
   311  	}
   312  
   313  	g := f.debug.g
   314  	defer func() { g.running = true }()
   315  
   316  	e := &DebugEvent{dbg, g.mode, f}
   317  	switch {
   318  	case g.mode == DebugTerminate:
   319  		dbg.cancel()
   320  		return true
   321  
   322  	case n.shouldBreak():
   323  		e.reason = DebugBreak
   324  
   325  	case g.mode == debugRun:
   326  		return false
   327  
   328  	case g.mode == DebugStepOut:
   329  		if g.fDepth >= g.fStep {
   330  			return false
   331  		}
   332  
   333  	case g.mode == DebugStepOver:
   334  		if g.fDepth > g.fStep {
   335  			return false
   336  		}
   337  	}
   338  	dbg.events(e)
   339  
   340  	g.running = false
   341  	select {
   342  	case <-g.resume:
   343  		return false
   344  	case <-dbg.context.Done():
   345  		return true
   346  	}
   347  }
   348  
   349  // Continue continues execution of the specified Go routine. Continue returns
   350  // ErrNotLive if there is no Go routine with the corresponding ID, or if it is not
   351  // live.
   352  func (dbg *Debugger) Continue(id int) error {
   353  	g, ok := dbg.getGoRoutine(id)
   354  	if !ok {
   355  		return ErrNotLive
   356  	}
   357  
   358  	g.mode = debugRun
   359  	g.resume <- struct{}{}
   360  	return nil
   361  }
   362  
   363  // update the exec mode of this routine.
   364  func (g *debugRoutine) setMode(reason DebugEventReason) {
   365  	if g.mode == DebugTerminate {
   366  		return
   367  	}
   368  
   369  	if g.mode == DebugEntry && reason == DebugEntry {
   370  		return
   371  	}
   372  
   373  	switch reason {
   374  	case DebugStepInto, DebugStepOver, DebugStepOut:
   375  		g.mode, g.fStep = reason, g.fDepth
   376  	default:
   377  		g.mode = DebugPause
   378  	}
   379  }
   380  
   381  // Step issues a stepInto, stepOver, or stepOut request to a stopped Go routine.
   382  // Step returns ErrRunning if the Go routine is running. Step returns ErrNotLive
   383  // if there is no Go routine with the corresponding ID, or if it is not live.
   384  func (dbg *Debugger) Step(id int, reason DebugEventReason) error {
   385  	g, ok := dbg.getGoRoutine(id)
   386  	if !ok {
   387  		return ErrNotLive
   388  	}
   389  
   390  	if g.running {
   391  		return ErrRunning
   392  	}
   393  
   394  	g.setMode(reason)
   395  	g.resume <- struct{}{}
   396  	return nil
   397  }
   398  
   399  // Interrupt issues a stepInto, stepOver, or stepOut request to a running Go
   400  // routine. Interrupt returns ErrRunning if the Go routine is running. Interrupt
   401  // returns ErrNotLive if there is no Go routine with the corresponding ID, or if
   402  // it is not live.
   403  func (dbg *Debugger) Interrupt(id int, reason DebugEventReason) bool {
   404  	g, ok := dbg.getGoRoutine(id)
   405  	if !ok {
   406  		return false
   407  	}
   408  
   409  	g.setMode(reason)
   410  	return true
   411  }
   412  
   413  // Terminate attempts to terminate the program.
   414  func (dbg *Debugger) Terminate() {
   415  	dbg.gLock.Lock()
   416  	g := dbg.gLive
   417  	dbg.gLive = nil
   418  	dbg.gLock.Unlock()
   419  
   420  	for _, g := range g {
   421  		g.mode = DebugTerminate
   422  		close(g.resume)
   423  	}
   424  }
   425  
   426  // BreakpointTarget is the target of a request to set breakpoints.
   427  type BreakpointTarget func(*Debugger, func(*node))
   428  
   429  // PathBreakpointTarget is used to set breapoints on compiled code by path. This
   430  // can be used to set breakpoints on code compiled with EvalPath, or source
   431  // packages loaded by Yaegi.
   432  func PathBreakpointTarget(path string) BreakpointTarget {
   433  	return func(dbg *Debugger, cb func(*node)) {
   434  		for _, r := range dbg.interp.roots {
   435  			f := dbg.interp.fset.File(r.pos)
   436  			if f != nil && f.Name() == path {
   437  				cb(r)
   438  				return
   439  			}
   440  		}
   441  	}
   442  }
   443  
   444  // ProgramBreakpointTarget is used to set breakpoints on a Program.
   445  func ProgramBreakpointTarget(prog *Program) BreakpointTarget {
   446  	return func(_ *Debugger, cb func(*node)) {
   447  		cb(prog.root)
   448  	}
   449  }
   450  
   451  // AllBreakpointTarget is used to set breakpoints on all compiled code. Do not
   452  // use with LineBreakpoint.
   453  func AllBreakpointTarget() BreakpointTarget {
   454  	return func(dbg *Debugger, cb func(*node)) {
   455  		for _, r := range dbg.interp.roots {
   456  			cb(r)
   457  		}
   458  	}
   459  }
   460  
   461  type breakpointSetup struct {
   462  	roots []*node
   463  	lines map[int]int
   464  	funcs map[string]int
   465  }
   466  
   467  // BreakpointRequest is a request to set a breakpoint.
   468  type BreakpointRequest func(*breakpointSetup, int)
   469  
   470  // LineBreakpoint requests a breakpoint on the given line.
   471  func LineBreakpoint(line int) BreakpointRequest {
   472  	return func(b *breakpointSetup, i int) {
   473  		b.lines[line] = i
   474  	}
   475  }
   476  
   477  // FunctionBreakpoint requests a breakpoint on the named function.
   478  func FunctionBreakpoint(name string) BreakpointRequest {
   479  	return func(b *breakpointSetup, i int) {
   480  		b.funcs[name] = i
   481  	}
   482  }
   483  
   484  // SetBreakpoints sets breakpoints for the given target. The returned array has
   485  // an entry for every request, in order. If a given breakpoint request cannot be
   486  // satisfied, the corresponding entry will be marked invalid. If the target
   487  // cannot be found, all entries will be marked invalid.
   488  func (dbg *Debugger) SetBreakpoints(target BreakpointTarget, requests ...BreakpointRequest) []Breakpoint {
   489  	// start with all breakpoints unverified
   490  	results := make([]Breakpoint, len(requests))
   491  
   492  	// prepare all the requests
   493  	setup := new(breakpointSetup)
   494  	target(dbg, func(root *node) {
   495  		setup.roots = append(setup.roots, root)
   496  		setup.lines = make(map[int]int, len(requests))
   497  		setup.funcs = make(map[string]int, len(requests))
   498  		for i, rq := range requests {
   499  			rq(setup, i)
   500  		}
   501  	})
   502  
   503  	// find breakpoints
   504  	for _, root := range setup.roots {
   505  		root.Walk(func(n *node) bool {
   506  			// function breakpoints
   507  			if len(setup.funcs) > 0 && n.kind == funcDecl {
   508  				// reset stale breakpoints
   509  				n.start.setBreakOnCall(false)
   510  
   511  				if i, ok := setup.funcs[n.child[1].ident]; ok && !results[i].Valid {
   512  					results[i].Valid = true
   513  					results[i].Position = dbg.interp.fset.Position(n.start.pos)
   514  					n.start.setBreakOnCall(true)
   515  					return true
   516  				}
   517  			}
   518  
   519  			// line breakpoints
   520  			if len(setup.lines) > 0 && n.pos.IsValid() && n.action != aNop && getExec(n) != nil {
   521  				// reset stale breakpoints
   522  				n.setBreakOnLine(false)
   523  
   524  				pos := dbg.interp.fset.Position(n.pos)
   525  				if i, ok := setup.lines[pos.Line]; ok && !results[i].Valid {
   526  					results[i].Valid = true
   527  					results[i].Position = pos
   528  					n.setBreakOnLine(true)
   529  					return true
   530  				}
   531  			}
   532  
   533  			return true
   534  		}, nil)
   535  	}
   536  
   537  	return results
   538  }
   539  
   540  // GoRoutines returns an array of live Go routines.
   541  func (dbg *Debugger) GoRoutines() []*DebugGoRoutine {
   542  	dbg.gLock.Lock()
   543  	r := make([]*DebugGoRoutine, 0, len(dbg.gLive))
   544  	for id := range dbg.gLive {
   545  		r = append(r, &DebugGoRoutine{id})
   546  	}
   547  	dbg.gLock.Unlock()
   548  	sort.Slice(r, func(i, j int) bool { return r[i].id < r[j].id })
   549  	return r
   550  }
   551  
   552  // ID returns the ID of the Go routine.
   553  func (r *DebugGoRoutine) ID() int { return r.id }
   554  
   555  // Name returns "Goroutine {ID}".
   556  func (r *DebugGoRoutine) Name() string { return fmt.Sprintf("Goroutine %d", r.id) }
   557  
   558  // GoRoutine returns the ID of the Go routine that generated the event.
   559  func (evt *DebugEvent) GoRoutine() int {
   560  	if evt.frame.debug == nil {
   561  		return 0
   562  	}
   563  	return evt.frame.debug.g.id
   564  }
   565  
   566  // Reason returns the reason for the event.
   567  func (evt *DebugEvent) Reason() DebugEventReason {
   568  	return evt.reason
   569  }
   570  
   571  // Walk the stack trace frames. The root frame is included if and only if it is
   572  // the only frame. Closure frames are rolled up into the following call frame.
   573  func (evt *DebugEvent) walkFrames(fn func([]*frame) bool) {
   574  	if evt.frame == evt.frame.root {
   575  		fn([]*frame{evt.frame})
   576  		return
   577  	}
   578  
   579  	var g *debugRoutine
   580  	if evt.frame.debug != nil {
   581  		g = evt.frame.debug.g
   582  	}
   583  
   584  	var frames []*frame
   585  	for f := evt.frame; f != nil && f != f.root && (f.debug == nil || f.debug.g == g); f = f.anc {
   586  		if f.debug == nil || f.debug.kind != frameCall {
   587  			frames = append(frames, f)
   588  			continue
   589  		}
   590  
   591  		if len(frames) > 0 {
   592  			if !fn(frames) {
   593  				return
   594  			}
   595  		}
   596  
   597  		frames = frames[:0]
   598  		frames = append(frames, f)
   599  	}
   600  
   601  	if len(frames) > 0 {
   602  		fn(frames)
   603  	}
   604  }
   605  
   606  // FrameDepth returns the number of call frames in the stack trace.
   607  func (evt *DebugEvent) FrameDepth() int {
   608  	if evt.frame == evt.frame.root {
   609  		return 1
   610  	}
   611  
   612  	var n int
   613  	evt.walkFrames(func([]*frame) bool { n++; return true })
   614  	return n
   615  }
   616  
   617  // Frames returns the call frames in the range [start, end).
   618  func (evt *DebugEvent) Frames(start, end int) []*DebugFrame {
   619  	count := end - start
   620  	if count < 0 {
   621  		return nil
   622  	}
   623  
   624  	frames := []*DebugFrame{}
   625  	evt.walkFrames(func(f []*frame) bool {
   626  		df := &DebugFrame{evt, make([]*frame, len(f))}
   627  		copy(df.frames, f)
   628  		frames = append(frames, df)
   629  		return len(frames) < count
   630  	})
   631  	return frames
   632  }
   633  
   634  // Name returns the name of the stack frame. For function calls to named
   635  // functions, this is the function name.
   636  func (f *DebugFrame) Name() string {
   637  	d := f.frames[0].debug
   638  	if d == nil {
   639  		return "<unknown>"
   640  	}
   641  	switch d.kind {
   642  	case frameRoot:
   643  		return "<init>"
   644  	case frameClosure:
   645  		return "<closure>"
   646  	case frameCall:
   647  		if d.name == "" {
   648  			return "<anonymous>"
   649  		}
   650  		return d.name
   651  	default:
   652  		return "<unknown>"
   653  	}
   654  }
   655  
   656  // Position returns the current position of the frame. This is effectively the
   657  // program counter/link register. May return `Position{}`.
   658  func (f *DebugFrame) Position() token.Position {
   659  	d := f.frames[0].debug
   660  	if d == nil || d.node == nil {
   661  		return token.Position{}
   662  	}
   663  	return f.event.debugger.interp.fset.Position(d.node.pos)
   664  }
   665  
   666  // Program returns the program associated with the current position of the
   667  // frame. May return nil.
   668  func (f *DebugFrame) Program() *Program {
   669  	d := f.frames[0].debug
   670  	if d == nil || d.node == nil {
   671  		return nil
   672  	}
   673  
   674  	return d.node.debug.program
   675  }
   676  
   677  // Scopes returns the variable scopes of the frame.
   678  func (f *DebugFrame) Scopes() []*DebugFrameScope {
   679  	s := make([]*DebugFrameScope, len(f.frames))
   680  	for i, f := range f.frames {
   681  		s[i] = &DebugFrameScope{f}
   682  	}
   683  	return s
   684  }
   685  
   686  // IsClosure returns true if this is the capture scope of a closure.
   687  func (f *DebugFrameScope) IsClosure() bool {
   688  	return f.frame.debug != nil && f.frame.debug.kind == frameClosure
   689  }
   690  
   691  // Variables returns the names and values of the variables of the scope.
   692  func (f *DebugFrameScope) Variables() []*DebugVariable {
   693  	d := f.frame.debug
   694  	if d == nil || d.scope == nil {
   695  		return nil
   696  	}
   697  
   698  	index := map[int]string{}
   699  	scanScope(d.scope, index)
   700  
   701  	m := make([]*DebugVariable, 0, len(f.frame.data))
   702  	for i, v := range f.frame.data {
   703  		if typ := v.Type(); typ.AssignableTo(rNodeType) || typ.Kind() == reflect.Ptr && typ.Elem().AssignableTo(rNodeType) {
   704  			continue
   705  		}
   706  		name, ok := index[i]
   707  		if !ok {
   708  			continue
   709  		}
   710  
   711  		m = append(m, &DebugVariable{name, v})
   712  	}
   713  	return m
   714  }
   715  
   716  func scanScope(sc *scope, index map[int]string) {
   717  	for name, sym := range sc.sym {
   718  		if _, ok := index[sym.index]; ok {
   719  			continue
   720  		}
   721  		index[sym.index] = name
   722  	}
   723  
   724  	for _, ch := range sc.child {
   725  		if ch.def != sc.def {
   726  			continue
   727  		}
   728  		scanScope(ch, index)
   729  	}
   730  }