source.go raw

   1  // Copyright 2013 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package ssa
   6  
   7  // This file defines utilities for working with source positions
   8  // or source-level named entities ("objects").
   9  
  10  // TODO(adonovan): test that {Value,Instruction}.Pos() positions match
  11  // the originating syntax, as specified.
  12  
  13  import (
  14  	"go/ast"
  15  	"go/token"
  16  	"go/types"
  17  )
  18  
  19  // EnclosingFunction returns the function that contains the syntax
  20  // node denoted by path.
  21  //
  22  // Syntax associated with package-level variable specifications is
  23  // enclosed by the package's init() function.
  24  //
  25  // Returns nil if not found; reasons might include:
  26  //   - the node is not enclosed by any function.
  27  //   - the node is within an anonymous function (FuncLit) and
  28  //     its SSA function has not been created yet
  29  //     (pkg.Build() has not yet been called).
  30  func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
  31  	// Start with package-level function...
  32  	fn := findEnclosingPackageLevelFunction(pkg, path)
  33  	if fn == nil {
  34  		return nil // not in any function
  35  	}
  36  
  37  	// ...then walk down the nested anonymous functions.
  38  	n := len(path)
  39  outer:
  40  	for i := range path {
  41  		if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
  42  			for _, anon := range fn.AnonFuncs {
  43  				if anon.Pos() == lit.Type.Func {
  44  					fn = anon
  45  					continue outer
  46  				}
  47  			}
  48  			// SSA function not found:
  49  			// - package not yet built, or maybe
  50  			// - builder skipped FuncLit in dead block
  51  			//   (in principle; but currently the Builder
  52  			//   generates even dead FuncLits).
  53  			return nil
  54  		}
  55  	}
  56  	return fn
  57  }
  58  
  59  // HasEnclosingFunction returns true if the AST node denoted by path
  60  // is contained within the declaration of some function or
  61  // package-level variable.
  62  //
  63  // Unlike EnclosingFunction, the behaviour of this function does not
  64  // depend on whether SSA code for pkg has been built, so it can be
  65  // used to quickly reject check inputs that will cause
  66  // EnclosingFunction to fail, prior to SSA building.
  67  func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
  68  	return findEnclosingPackageLevelFunction(pkg, path) != nil
  69  }
  70  
  71  // findEnclosingPackageLevelFunction returns the Function
  72  // corresponding to the package-level function enclosing path.
  73  func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
  74  	if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
  75  		switch decl := path[n-2].(type) {
  76  		case *ast.GenDecl:
  77  			if decl.Tok == token.VAR && n >= 3 {
  78  				// Package-level 'var' initializer.
  79  				return pkg.init
  80  			}
  81  
  82  		case *ast.FuncDecl:
  83  			if decl.Recv == nil && decl.Name.Name == "init" {
  84  				// Explicit init() function.
  85  				for _, b := range pkg.init.Blocks {
  86  					for _, instr := range b.Instrs {
  87  						if instr, ok := instr.(*Call); ok {
  88  							if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
  89  								return callee
  90  							}
  91  						}
  92  					}
  93  				}
  94  				// Hack: return non-nil when SSA is not yet
  95  				// built so that HasEnclosingFunction works.
  96  				return pkg.init
  97  			}
  98  			// Declared function/method.
  99  			return findNamedFunc(pkg, decl.Name.NamePos)
 100  		}
 101  	}
 102  	return nil // not in any function
 103  }
 104  
 105  // findNamedFunc returns the named function whose FuncDecl.Ident is at
 106  // position pos.
 107  func findNamedFunc(pkg *Package, pos token.Pos) *Function {
 108  	// Look at all package members and method sets of named types.
 109  	// Not very efficient.
 110  	for _, mem := range pkg.Members {
 111  		switch mem := mem.(type) {
 112  		case *Function:
 113  			if mem.Pos() == pos {
 114  				return mem
 115  			}
 116  		case *Type:
 117  			mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
 118  			for i, n := 0, mset.Len(); i < n; i++ {
 119  				// Don't call Program.Method: avoid creating wrappers.
 120  				obj := mset.At(i).Obj().(*types.Func)
 121  				if obj.Pos() == pos {
 122  					// obj from MethodSet may not be the origin type.
 123  					m := obj.Origin()
 124  					return pkg.objects[m].(*Function)
 125  				}
 126  			}
 127  		}
 128  	}
 129  	return nil
 130  }
 131  
 132  // ValueForExpr returns the SSA Value that corresponds to non-constant
 133  // expression e.
 134  //
 135  // It returns nil if no value was found, e.g.
 136  //   - the expression is not lexically contained within f;
 137  //   - f was not built with debug information; or
 138  //   - e is a constant expression.  (For efficiency, no debug
 139  //     information is stored for constants. Use
 140  //     go/types.Info.Types[e].Value instead.)
 141  //   - e is a reference to nil or a built-in function.
 142  //   - the value was optimised away.
 143  //
 144  // If e is an addressable expression used in an lvalue context,
 145  // value is the address denoted by e, and isAddr is true.
 146  //
 147  // The types of e (or &e, if isAddr) and the result are equal
 148  // (modulo "untyped" bools resulting from comparisons).
 149  //
 150  // (Tip: to find the ssa.Value given a source position, use
 151  // astutil.PathEnclosingInterval to locate the ast.Node, then
 152  // EnclosingFunction to locate the Function, then ValueForExpr to find
 153  // the ssa.Value.)
 154  func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
 155  	if f.debugInfo() { // (opt)
 156  		e = ast.Unparen(e)
 157  		for _, b := range f.Blocks {
 158  			for _, instr := range b.Instrs {
 159  				if ref, ok := instr.(*DebugRef); ok {
 160  					if ref.Expr == e {
 161  						return ref.X, ref.IsAddr
 162  					}
 163  				}
 164  			}
 165  		}
 166  	}
 167  	return
 168  }
 169  
 170  // --- Lookup functions for source-level named entities (types.Objects) ---
 171  
 172  // Package returns the SSA Package corresponding to the specified
 173  // type-checker package. It returns nil if no such Package was
 174  // created by a prior call to prog.CreatePackage.
 175  func (prog *Program) Package(pkg *types.Package) *Package {
 176  	return prog.packages[pkg]
 177  }
 178  
 179  // packageLevelMember returns the package-level member corresponding
 180  // to the specified symbol, which may be a package-level const
 181  // (*NamedConst), var (*Global) or func/method (*Function) of some
 182  // package in prog.
 183  //
 184  // It returns nil if the object belongs to a package that has not been
 185  // created by prog.CreatePackage.
 186  func (prog *Program) packageLevelMember(obj types.Object) Member {
 187  	if pkg, ok := prog.packages[obj.Pkg()]; ok {
 188  		return pkg.objects[obj]
 189  	}
 190  	return nil
 191  }
 192  
 193  // FuncValue returns the SSA function or (non-interface) method
 194  // denoted by the specified func symbol. It returns nil if the symbol
 195  // denotes an interface method, or belongs to a package that was not
 196  // created by prog.CreatePackage.
 197  func (prog *Program) FuncValue(obj *types.Func) *Function {
 198  	fn, _ := prog.packageLevelMember(obj).(*Function)
 199  	return fn
 200  }
 201  
 202  // ConstValue returns the SSA constant denoted by the specified const symbol.
 203  func (prog *Program) ConstValue(obj *types.Const) *Const {
 204  	// TODO(adonovan): opt: share (don't reallocate)
 205  	// Consts for const objects and constant ast.Exprs.
 206  
 207  	// Universal constant? {true,false,nil}
 208  	if obj.Parent() == types.Universe {
 209  		return NewConst(obj.Val(), obj.Type())
 210  	}
 211  	// Package-level named constant?
 212  	if v := prog.packageLevelMember(obj); v != nil {
 213  		return v.(*NamedConst).Value
 214  	}
 215  	return NewConst(obj.Val(), obj.Type())
 216  }
 217  
 218  // VarValue returns the SSA Value that corresponds to a specific
 219  // identifier denoting the specified var symbol.
 220  //
 221  // VarValue returns nil if a local variable was not found, perhaps
 222  // because its package was not built, the debug information was not
 223  // requested during SSA construction, or the value was optimized away.
 224  //
 225  // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
 226  // and that ident must resolve to obj.
 227  //
 228  // pkg is the package enclosing the reference.  (A reference to a var
 229  // always occurs within a function, so we need to know where to find it.)
 230  //
 231  // If the identifier is a field selector and its base expression is
 232  // non-addressable, then VarValue returns the value of that field.
 233  // For example:
 234  //
 235  //	func f() struct {x int}
 236  //	f().x  // VarValue(x) returns a *Field instruction of type int
 237  //
 238  // All other identifiers denote addressable locations (variables).
 239  // For them, VarValue may return either the variable's address or its
 240  // value, even when the expression is evaluated only for its value; the
 241  // situation is reported by isAddr, the second component of the result.
 242  //
 243  // If !isAddr, the returned value is the one associated with the
 244  // specific identifier.  For example,
 245  //
 246  //	var x int    // VarValue(x) returns Const 0 here
 247  //	x = 1        // VarValue(x) returns Const 1 here
 248  //
 249  // It is not specified whether the value or the address is returned in
 250  // any particular case, as it may depend upon optimizations performed
 251  // during SSA code generation, such as registerization, constant
 252  // folding, avoidance of materialization of subexpressions, etc.
 253  func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
 254  	// All references to a var are local to some function, possibly init.
 255  	fn := EnclosingFunction(pkg, ref)
 256  	if fn == nil {
 257  		return // e.g. def of struct field; SSA not built?
 258  	}
 259  
 260  	id := ref[0].(*ast.Ident)
 261  
 262  	// Defining ident of a parameter?
 263  	if id.Pos() == obj.Pos() {
 264  		for _, param := range fn.Params {
 265  			if param.Object() == obj {
 266  				return param, false
 267  			}
 268  		}
 269  	}
 270  
 271  	// Other ident?
 272  	for _, b := range fn.Blocks {
 273  		for _, instr := range b.Instrs {
 274  			if dr, ok := instr.(*DebugRef); ok {
 275  				if dr.Pos() == id.Pos() {
 276  					return dr.X, dr.IsAddr
 277  				}
 278  			}
 279  		}
 280  	}
 281  
 282  	// Defining ident of package-level var?
 283  	if v := prog.packageLevelMember(obj); v != nil {
 284  		return v.(*Global), true
 285  	}
 286  
 287  	return // e.g. debug info not requested, or var optimized away
 288  }
 289