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

     1  package interp
     2  
     3  import (
     4  	"log"
     5  	"reflect"
     6  	"strconv"
     7  )
     8  
     9  // A sKind represents the kind of symbol.
    10  type sKind uint
    11  
    12  // Symbol kinds for the Go interpreter.
    13  const (
    14  	undefSym   sKind = iota
    15  	binSym           // Binary from runtime
    16  	bltnSym          // Builtin
    17  	constSym         // Constant
    18  	funcSym          // Function
    19  	labelSym         // Label
    20  	pkgSym           // Package
    21  	typeSym          // Type
    22  	varTypeSym       // Variable type (generic)
    23  	varSym           // Variable
    24  )
    25  
    26  var symKinds = [...]string{
    27  	undefSym:   "undefSym",
    28  	binSym:     "binSym",
    29  	bltnSym:    "bltnSym",
    30  	constSym:   "constSym",
    31  	funcSym:    "funcSym",
    32  	labelSym:   "labelSym",
    33  	pkgSym:     "pkgSym",
    34  	typeSym:    "typeSym",
    35  	varTypeSym: "varTypeSym",
    36  	varSym:     "varSym",
    37  }
    38  
    39  func (k sKind) String() string {
    40  	if k < sKind(len(symKinds)) {
    41  		return symKinds[k]
    42  	}
    43  	return "SymKind(" + strconv.Itoa(int(k)) + ")"
    44  }
    45  
    46  // A symbol represents an interpreter object such as type, constant, var, func,
    47  // label, builtin or binary object. Symbols are defined within a scope.
    48  type symbol struct {
    49  	kind    sKind
    50  	typ     *itype        // Type of value
    51  	node    *node         // Node value if index is negative
    52  	from    []*node       // list of goto nodes jumping to this label node, or nil
    53  	recv    *receiver     // receiver node value, if sym refers to a method
    54  	index   int           // index of value in frame or -1
    55  	rval    reflect.Value // default value (used for constants)
    56  	builtin bltnGenerator // Builtin function or nil
    57  	global  bool          // true if symbol is defined in global space
    58  }
    59  
    60  // scope type stores symbols in maps, and frame layout as array of types
    61  // The purposes of scopes are to manage the visibility of each symbol
    62  // and to store the memory frame layout information (type and index in frame)
    63  // at each level (global, package, functions)
    64  //
    65  // scopes are organized in a stack fashion: a first scope (universe) is created
    66  // once at global level, and for each block (package, func, for, etc...), a new
    67  // scope is pushed at entry, and poped at exit.
    68  //
    69  // Nested scopes with the same level value use the same frame: it allows to have
    70  // exactly one frame per function, with a fixed position for each variable (named
    71  // or not), no matter the inner complexity (number of nested blocks in the function)
    72  //
    73  // In symbols, the index value corresponds to the index in scope.types, and at
    74  // execution to the index in frame, created exactly from the types layout.
    75  type scope struct {
    76  	anc         *scope             // ancestor upper scope
    77  	child       []*scope           // included scopes
    78  	def         *node              // function definition node this scope belongs to, or nil
    79  	loop        *node              // loop exit node for break statement
    80  	loopRestart *node              // loop restart node for continue statement
    81  	pkgID       string             // unique id of package in which scope is defined
    82  	pkgName     string             // package name for the package
    83  	types       []reflect.Type     // frame layout, may be shared by same level scopes
    84  	level       int                // frame level: number of frame indirections to access var during execution
    85  	sym         map[string]*symbol // map of symbols defined in this current scope
    86  	global      bool               // true if scope refers to global space (single frame for universe and package level scopes)
    87  	iota        int                // iota value in this scope
    88  }
    89  
    90  // push creates a new child scope and chain it to the current one.
    91  func (s *scope) push(indirect bool) *scope {
    92  	sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}}
    93  	s.child = append(s.child, sc)
    94  	if indirect {
    95  		sc.types = []reflect.Type{}
    96  		sc.level = s.level + 1
    97  	} else {
    98  		// Propagate size, types, def and global as scopes at same level share the same frame.
    99  		sc.types = s.types
   100  		sc.def = s.def
   101  		sc.global = s.global
   102  		sc.level = s.level
   103  	}
   104  	// inherit loop state and pkgID from ancestor
   105  	sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
   106  	return sc
   107  }
   108  
   109  func (s *scope) pushBloc() *scope { return s.push(false) }
   110  func (s *scope) pushFunc() *scope { return s.push(true) }
   111  
   112  func (s *scope) pop() *scope {
   113  	if s.level == s.anc.level {
   114  		// Propagate size and types, as scopes at same level share the same frame.
   115  		s.anc.types = s.types
   116  	}
   117  	return s.anc
   118  }
   119  
   120  func (s *scope) upperLevel() *scope {
   121  	level := s.level
   122  	for s != nil && s.level == level {
   123  		s = s.anc
   124  	}
   125  	return s
   126  }
   127  
   128  // lookup searches for a symbol in the current scope, and upper ones if not found
   129  // it returns the symbol, the number of indirections level from the current scope
   130  // and status (false if no result).
   131  func (s *scope) lookup(ident string) (*symbol, int, bool) {
   132  	level := s.level
   133  	for {
   134  		if sym, ok := s.sym[ident]; ok {
   135  			if sym.global {
   136  				return sym, globalFrame, true
   137  			}
   138  			return sym, level - s.level, true
   139  		}
   140  		if s.anc == nil {
   141  			break
   142  		}
   143  		s = s.anc
   144  	}
   145  	return nil, 0, false
   146  }
   147  
   148  func (s *scope) rangeChanType(n *node) *itype {
   149  	if sym, _, found := s.lookup(n.child[1].ident); found {
   150  		if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
   151  			return t
   152  		}
   153  	}
   154  
   155  	c := n.child[1]
   156  	if c.typ == nil {
   157  		return nil
   158  	}
   159  	switch {
   160  	case c.typ.cat == chanT, c.typ.cat == chanRecvT:
   161  		return c.typ
   162  	case c.typ.cat == valueT && c.typ.rtype.Kind() == reflect.Chan:
   163  		dir := chanSendRecv
   164  		switch c.typ.rtype.ChanDir() {
   165  		case reflect.RecvDir:
   166  			dir = chanRecv
   167  		case reflect.SendDir:
   168  			dir = chanSend
   169  		}
   170  		return chanOf(valueTOf(c.typ.rtype.Elem()), dir)
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  // fixType returns the input type, or a valid default type for untyped constant.
   177  func (s *scope) fixType(t *itype) *itype {
   178  	if !t.untyped || t.cat != valueT {
   179  		return t
   180  	}
   181  	switch typ := t.TypeOf(); typ.Kind() {
   182  	case reflect.Int64:
   183  		return s.getType("int")
   184  	case reflect.Uint64:
   185  		return s.getType("uint")
   186  	case reflect.Float64:
   187  		return s.getType("float64")
   188  	case reflect.Complex128:
   189  		return s.getType("complex128")
   190  	}
   191  	return t
   192  }
   193  
   194  func (s *scope) getType(ident string) *itype {
   195  	var t *itype
   196  	if sym, _, found := s.lookup(ident); found {
   197  		if sym.kind == typeSym {
   198  			t = sym.typ
   199  		}
   200  	}
   201  	return t
   202  }
   203  
   204  // add adds a type to the scope types array, and returns its index.
   205  func (s *scope) add(typ *itype) (index int) {
   206  	if typ == nil {
   207  		log.Panic("nil type")
   208  	}
   209  	index = len(s.types)
   210  	t := typ.frameType()
   211  	if t == nil {
   212  		log.Panic("nil reflect type")
   213  	}
   214  	s.types = append(s.types, t)
   215  	return
   216  }
   217  
   218  func (interp *Interpreter) initScopePkg(pkgID, pkgName string) *scope {
   219  	sc := interp.universe
   220  
   221  	interp.mutex.Lock()
   222  	if _, ok := interp.scopes[pkgID]; !ok {
   223  		interp.scopes[pkgID] = sc.pushBloc()
   224  	}
   225  	sc = interp.scopes[pkgID]
   226  	sc.pkgID = pkgID
   227  	sc.pkgName = pkgName
   228  	interp.mutex.Unlock()
   229  	return sc
   230  }
   231  
   232  // Globals returns a map of global variables and constants in the main package.
   233  func (interp *Interpreter) Globals() map[string]reflect.Value {
   234  	syms := map[string]reflect.Value{}
   235  	interp.mutex.RLock()
   236  	defer interp.mutex.RUnlock()
   237  
   238  	v, ok := interp.srcPkg["main"]
   239  	if !ok {
   240  		return syms
   241  	}
   242  
   243  	for n, s := range v {
   244  		switch s.kind {
   245  		case constSym:
   246  			syms[n] = s.rval
   247  		case varSym:
   248  			syms[n] = interp.frame.data[s.index]
   249  		}
   250  	}
   251  
   252  	return syms
   253  }