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