295 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2013 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package ssa
 | |
| 
 | |
| // This file defines utilities for working with source positions
 | |
| // or source-level named entities ("objects").
 | |
| 
 | |
| // TODO(adonovan): test that {Value,Instruction}.Pos() positions match
 | |
| // the originating syntax, as specified.
 | |
| 
 | |
| import (
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 
 | |
| 	"golang.org/x/tools/go/types"
 | |
| )
 | |
| 
 | |
| // EnclosingFunction returns the function that contains the syntax
 | |
| // node denoted by path.
 | |
| //
 | |
| // Syntax associated with package-level variable specifications is
 | |
| // enclosed by the package's init() function.
 | |
| //
 | |
| // Returns nil if not found; reasons might include:
 | |
| //    - the node is not enclosed by any function.
 | |
| //    - the node is within an anonymous function (FuncLit) and
 | |
| //      its SSA function has not been created yet
 | |
| //      (pkg.Build() has not yet been called).
 | |
| //
 | |
| func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
 | |
| 	// Start with package-level function...
 | |
| 	fn := findEnclosingPackageLevelFunction(pkg, path)
 | |
| 	if fn == nil {
 | |
| 		return nil // not in any function
 | |
| 	}
 | |
| 
 | |
| 	// ...then walk down the nested anonymous functions.
 | |
| 	n := len(path)
 | |
| outer:
 | |
| 	for i := range path {
 | |
| 		if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
 | |
| 			for _, anon := range fn.AnonFuncs {
 | |
| 				if anon.Pos() == lit.Type.Func {
 | |
| 					fn = anon
 | |
| 					continue outer
 | |
| 				}
 | |
| 			}
 | |
| 			// SSA function not found:
 | |
| 			// - package not yet built, or maybe
 | |
| 			// - builder skipped FuncLit in dead block
 | |
| 			//   (in principle; but currently the Builder
 | |
| 			//   generates even dead FuncLits).
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 	return fn
 | |
| }
 | |
| 
 | |
| // HasEnclosingFunction returns true if the AST node denoted by path
 | |
| // is contained within the declaration of some function or
 | |
| // package-level variable.
 | |
| //
 | |
| // Unlike EnclosingFunction, the behaviour of this function does not
 | |
| // depend on whether SSA code for pkg has been built, so it can be
 | |
| // used to quickly reject check inputs that will cause
 | |
| // EnclosingFunction to fail, prior to SSA building.
 | |
| //
 | |
| func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
 | |
| 	return findEnclosingPackageLevelFunction(pkg, path) != nil
 | |
| }
 | |
| 
 | |
| // findEnclosingPackageLevelFunction returns the Function
 | |
| // corresponding to the package-level function enclosing path.
 | |
| //
 | |
| func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
 | |
| 	if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
 | |
| 		switch decl := path[n-2].(type) {
 | |
| 		case *ast.GenDecl:
 | |
| 			if decl.Tok == token.VAR && n >= 3 {
 | |
| 				// Package-level 'var' initializer.
 | |
| 				return pkg.init
 | |
| 			}
 | |
| 
 | |
| 		case *ast.FuncDecl:
 | |
| 			if decl.Recv == nil && decl.Name.Name == "init" {
 | |
| 				// Explicit init() function.
 | |
| 				for _, b := range pkg.init.Blocks {
 | |
| 					for _, instr := range b.Instrs {
 | |
| 						if instr, ok := instr.(*Call); ok {
 | |
| 							if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
 | |
| 								return callee
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				// Hack: return non-nil when SSA is not yet
 | |
| 				// built so that HasEnclosingFunction works.
 | |
| 				return pkg.init
 | |
| 			}
 | |
| 			// Declared function/method.
 | |
| 			return findNamedFunc(pkg, decl.Name.NamePos)
 | |
| 		}
 | |
| 	}
 | |
| 	return nil // not in any function
 | |
| }
 | |
| 
 | |
| // findNamedFunc returns the named function whose FuncDecl.Ident is at
 | |
| // position pos.
 | |
| //
 | |
| func findNamedFunc(pkg *Package, pos token.Pos) *Function {
 | |
| 	// Look at all package members and method sets of named types.
 | |
| 	// Not very efficient.
 | |
| 	for _, mem := range pkg.Members {
 | |
| 		switch mem := mem.(type) {
 | |
| 		case *Function:
 | |
| 			if mem.Pos() == pos {
 | |
| 				return mem
 | |
| 			}
 | |
| 		case *Type:
 | |
| 			mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
 | |
| 			for i, n := 0, mset.Len(); i < n; i++ {
 | |
| 				// Don't call Program.Method: avoid creating wrappers.
 | |
| 				obj := mset.At(i).Obj().(*types.Func)
 | |
| 				if obj.Pos() == pos {
 | |
| 					return pkg.values[obj].(*Function)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // ValueForExpr returns the SSA Value that corresponds to non-constant
 | |
| // expression e.
 | |
| //
 | |
| // It returns nil if no value was found, e.g.
 | |
| //    - the expression is not lexically contained within f;
 | |
| //    - f was not built with debug information; or
 | |
| //    - e is a constant expression.  (For efficiency, no debug
 | |
| //      information is stored for constants. Use
 | |
| //      loader.PackageInfo.ValueOf(e) instead.)
 | |
| //    - e is a reference to nil or a built-in function.
 | |
| //    - the value was optimised away.
 | |
| //
 | |
| // If e is an addressable expression used an an lvalue context,
 | |
| // value is the address denoted by e, and isAddr is true.
 | |
| //
 | |
| // The types of e (or &e, if isAddr) and the result are equal
 | |
| // (modulo "untyped" bools resulting from comparisons).
 | |
| //
 | |
| // (Tip: to find the ssa.Value given a source position, use
 | |
| // importer.PathEnclosingInterval to locate the ast.Node, then
 | |
| // EnclosingFunction to locate the Function, then ValueForExpr to find
 | |
| // the ssa.Value.)
 | |
| //
 | |
| func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
 | |
| 	if f.debugInfo() { // (opt)
 | |
| 		e = unparen(e)
 | |
| 		for _, b := range f.Blocks {
 | |
| 			for _, instr := range b.Instrs {
 | |
| 				if ref, ok := instr.(*DebugRef); ok {
 | |
| 					if ref.Expr == e {
 | |
| 						return ref.X, ref.IsAddr
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // --- Lookup functions for source-level named entities (types.Objects) ---
 | |
| 
 | |
| // Package returns the SSA Package corresponding to the specified
 | |
| // type-checker package object.
 | |
| // It returns nil if no such SSA package has been created.
 | |
| //
 | |
| func (prog *Program) Package(obj *types.Package) *Package {
 | |
| 	return prog.packages[obj]
 | |
| }
 | |
| 
 | |
| // packageLevelValue returns the package-level value corresponding to
 | |
| // the specified named object, which may be a package-level const
 | |
| // (*Const), var (*Global) or func (*Function) of some package in
 | |
| // prog.  It returns nil if the object is not found.
 | |
| //
 | |
| func (prog *Program) packageLevelValue(obj types.Object) Value {
 | |
| 	if pkg, ok := prog.packages[obj.Pkg()]; ok {
 | |
| 		return pkg.values[obj]
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // FuncValue returns the concrete Function denoted by the source-level
 | |
| // named function obj, or nil if obj denotes an interface method.
 | |
| //
 | |
| // TODO(adonovan): check the invariant that obj.Type() matches the
 | |
| // result's Signature, both in the params/results and in the receiver.
 | |
| //
 | |
| func (prog *Program) FuncValue(obj *types.Func) *Function {
 | |
| 	fn, _ := prog.packageLevelValue(obj).(*Function)
 | |
| 	return fn
 | |
| }
 | |
| 
 | |
| // ConstValue returns the SSA Value denoted by the source-level named
 | |
| // constant obj.
 | |
| //
 | |
| func (prog *Program) ConstValue(obj *types.Const) *Const {
 | |
| 	// TODO(adonovan): opt: share (don't reallocate)
 | |
| 	// Consts for const objects and constant ast.Exprs.
 | |
| 
 | |
| 	// Universal constant? {true,false,nil}
 | |
| 	if obj.Parent() == types.Universe {
 | |
| 		return NewConst(obj.Val(), obj.Type())
 | |
| 	}
 | |
| 	// Package-level named constant?
 | |
| 	if v := prog.packageLevelValue(obj); v != nil {
 | |
| 		return v.(*Const)
 | |
| 	}
 | |
| 	return NewConst(obj.Val(), obj.Type())
 | |
| }
 | |
| 
 | |
| // VarValue returns the SSA Value that corresponds to a specific
 | |
| // identifier denoting the source-level named variable obj.
 | |
| //
 | |
| // VarValue returns nil if a local variable was not found, perhaps
 | |
| // because its package was not built, the debug information was not
 | |
| // requested during SSA construction, or the value was optimized away.
 | |
| //
 | |
| // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
 | |
| // and that ident must resolve to obj.
 | |
| //
 | |
| // pkg is the package enclosing the reference.  (A reference to a var
 | |
| // always occurs within a function, so we need to know where to find it.)
 | |
| //
 | |
| // If the identifier is a field selector and its base expression is
 | |
| // non-addressable, then VarValue returns the value of that field.
 | |
| // For example:
 | |
| //    func f() struct {x int}
 | |
| //    f().x  // VarValue(x) returns a *Field instruction of type int
 | |
| //
 | |
| // All other identifiers denote addressable locations (variables).
 | |
| // For them, VarValue may return either the variable's address or its
 | |
| // value, even when the expression is evaluated only for its value; the
 | |
| // situation is reported by isAddr, the second component of the result.
 | |
| //
 | |
| // If !isAddr, the returned value is the one associated with the
 | |
| // specific identifier.  For example,
 | |
| //       var x int    // VarValue(x) returns Const 0 here
 | |
| //       x = 1        // VarValue(x) returns Const 1 here
 | |
| //
 | |
| // It is not specified whether the value or the address is returned in
 | |
| // any particular case, as it may depend upon optimizations performed
 | |
| // during SSA code generation, such as registerization, constant
 | |
| // folding, avoidance of materialization of subexpressions, etc.
 | |
| //
 | |
| func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
 | |
| 	// All references to a var are local to some function, possibly init.
 | |
| 	fn := EnclosingFunction(pkg, ref)
 | |
| 	if fn == nil {
 | |
| 		return // e.g. def of struct field; SSA not built?
 | |
| 	}
 | |
| 
 | |
| 	id := ref[0].(*ast.Ident)
 | |
| 
 | |
| 	// Defining ident of a parameter?
 | |
| 	if id.Pos() == obj.Pos() {
 | |
| 		for _, param := range fn.Params {
 | |
| 			if param.Object() == obj {
 | |
| 				return param, false
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Other ident?
 | |
| 	for _, b := range fn.Blocks {
 | |
| 		for _, instr := range b.Instrs {
 | |
| 			if dr, ok := instr.(*DebugRef); ok {
 | |
| 				if dr.Pos() == id.Pos() {
 | |
| 					return dr.X, dr.IsAddr
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Defining ident of package-level var?
 | |
| 	if v := prog.packageLevelValue(obj); v != nil {
 | |
| 		return v.(*Global), true
 | |
| 	}
 | |
| 
 | |
| 	return // e.g. debug info not requested, or var optimized away
 | |
| }
 |