| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- // 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"
- "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
- // go/types.Info.Types[e].Value instead.)
- // - e is a reference to nil or a built-in function.
- // - the value was optimised away.
- //
- // If e is an addressable expression used in 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
- // astutil.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
- }
|