src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

source.go (9467B)


      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 }