...

Source file src/golang.org/x/tools/go/ssa/source.go

Documentation: golang.org/x/tools/go/ssa

     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 = 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 id 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  

View as plain text