github.com/jd-ly/tools@v0.5.7/go/ssa/create.go (about)

     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 implements the CREATE phase of SSA construction.
     8  // See builder.go for explanation.
     9  
    10  import (
    11  	"fmt"
    12  	"go/ast"
    13  	"go/token"
    14  	"go/types"
    15  	"os"
    16  	"sync"
    17  
    18  	"github.com/jd-ly/tools/go/types/typeutil"
    19  )
    20  
    21  // NewProgram returns a new SSA Program.
    22  //
    23  // mode controls diagnostics and checking during SSA construction.
    24  //
    25  func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
    26  	prog := &Program{
    27  		Fset:     fset,
    28  		imported: make(map[string]*Package),
    29  		packages: make(map[*types.Package]*Package),
    30  		thunks:   make(map[selectionKey]*Function),
    31  		bounds:   make(map[*types.Func]*Function),
    32  		mode:     mode,
    33  	}
    34  
    35  	h := typeutil.MakeHasher() // protected by methodsMu, in effect
    36  	prog.methodSets.SetHasher(h)
    37  	prog.canon.SetHasher(h)
    38  
    39  	return prog
    40  }
    41  
    42  // memberFromObject populates package pkg with a member for the
    43  // typechecker object obj.
    44  //
    45  // For objects from Go source code, syntax is the associated syntax
    46  // tree (for funcs and vars only); it will be used during the build
    47  // phase.
    48  //
    49  func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
    50  	name := obj.Name()
    51  	switch obj := obj.(type) {
    52  	case *types.Builtin:
    53  		if pkg.Pkg != types.Unsafe {
    54  			panic("unexpected builtin object: " + obj.String())
    55  		}
    56  
    57  	case *types.TypeName:
    58  		pkg.Members[name] = &Type{
    59  			object: obj,
    60  			pkg:    pkg,
    61  		}
    62  
    63  	case *types.Const:
    64  		c := &NamedConst{
    65  			object: obj,
    66  			Value:  NewConst(obj.Val(), obj.Type()),
    67  			pkg:    pkg,
    68  		}
    69  		pkg.values[obj] = c.Value
    70  		pkg.Members[name] = c
    71  
    72  	case *types.Var:
    73  		g := &Global{
    74  			Pkg:    pkg,
    75  			name:   name,
    76  			object: obj,
    77  			typ:    types.NewPointer(obj.Type()), // address
    78  			pos:    obj.Pos(),
    79  		}
    80  		pkg.values[obj] = g
    81  		pkg.Members[name] = g
    82  
    83  	case *types.Func:
    84  		sig := obj.Type().(*types.Signature)
    85  		if sig.Recv() == nil && name == "init" {
    86  			pkg.ninit++
    87  			name = fmt.Sprintf("init#%d", pkg.ninit)
    88  		}
    89  		fn := &Function{
    90  			name:      name,
    91  			object:    obj,
    92  			Signature: sig,
    93  			syntax:    syntax,
    94  			pos:       obj.Pos(),
    95  			Pkg:       pkg,
    96  			Prog:      pkg.Prog,
    97  		}
    98  		if syntax == nil {
    99  			fn.Synthetic = "loaded from gc object file"
   100  		}
   101  
   102  		pkg.values[obj] = fn
   103  		if sig.Recv() == nil {
   104  			pkg.Members[name] = fn // package-level function
   105  		}
   106  
   107  	default: // (incl. *types.Package)
   108  		panic("unexpected Object type: " + obj.String())
   109  	}
   110  }
   111  
   112  // membersFromDecl populates package pkg with members for each
   113  // typechecker object (var, func, const or type) associated with the
   114  // specified decl.
   115  //
   116  func membersFromDecl(pkg *Package, decl ast.Decl) {
   117  	switch decl := decl.(type) {
   118  	case *ast.GenDecl: // import, const, type or var
   119  		switch decl.Tok {
   120  		case token.CONST:
   121  			for _, spec := range decl.Specs {
   122  				for _, id := range spec.(*ast.ValueSpec).Names {
   123  					if !isBlankIdent(id) {
   124  						memberFromObject(pkg, pkg.info.Defs[id], nil)
   125  					}
   126  				}
   127  			}
   128  
   129  		case token.VAR:
   130  			for _, spec := range decl.Specs {
   131  				for _, id := range spec.(*ast.ValueSpec).Names {
   132  					if !isBlankIdent(id) {
   133  						memberFromObject(pkg, pkg.info.Defs[id], spec)
   134  					}
   135  				}
   136  			}
   137  
   138  		case token.TYPE:
   139  			for _, spec := range decl.Specs {
   140  				id := spec.(*ast.TypeSpec).Name
   141  				if !isBlankIdent(id) {
   142  					memberFromObject(pkg, pkg.info.Defs[id], nil)
   143  				}
   144  			}
   145  		}
   146  
   147  	case *ast.FuncDecl:
   148  		id := decl.Name
   149  		if !isBlankIdent(id) {
   150  			memberFromObject(pkg, pkg.info.Defs[id], decl)
   151  		}
   152  	}
   153  }
   154  
   155  // CreatePackage constructs and returns an SSA Package from the
   156  // specified type-checked, error-free file ASTs, and populates its
   157  // Members mapping.
   158  //
   159  // importable determines whether this package should be returned by a
   160  // subsequent call to ImportedPackage(pkg.Path()).
   161  //
   162  // The real work of building SSA form for each function is not done
   163  // until a subsequent call to Package.Build().
   164  //
   165  func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
   166  	p := &Package{
   167  		Prog:    prog,
   168  		Members: make(map[string]Member),
   169  		values:  make(map[types.Object]Value),
   170  		Pkg:     pkg,
   171  		info:    info,  // transient (CREATE and BUILD phases)
   172  		files:   files, // transient (CREATE and BUILD phases)
   173  	}
   174  
   175  	// Add init() function.
   176  	p.init = &Function{
   177  		name:      "init",
   178  		Signature: new(types.Signature),
   179  		Synthetic: "package initializer",
   180  		Pkg:       p,
   181  		Prog:      prog,
   182  	}
   183  	p.Members[p.init.name] = p.init
   184  
   185  	// CREATE phase.
   186  	// Allocate all package members: vars, funcs, consts and types.
   187  	if len(files) > 0 {
   188  		// Go source package.
   189  		for _, file := range files {
   190  			for _, decl := range file.Decls {
   191  				membersFromDecl(p, decl)
   192  			}
   193  		}
   194  	} else {
   195  		// GC-compiled binary package (or "unsafe")
   196  		// No code.
   197  		// No position information.
   198  		scope := p.Pkg.Scope()
   199  		for _, name := range scope.Names() {
   200  			obj := scope.Lookup(name)
   201  			memberFromObject(p, obj, nil)
   202  			if obj, ok := obj.(*types.TypeName); ok {
   203  				if named, ok := obj.Type().(*types.Named); ok {
   204  					for i, n := 0, named.NumMethods(); i < n; i++ {
   205  						memberFromObject(p, named.Method(i), nil)
   206  					}
   207  				}
   208  			}
   209  		}
   210  	}
   211  
   212  	if prog.mode&BareInits == 0 {
   213  		// Add initializer guard variable.
   214  		initguard := &Global{
   215  			Pkg:  p,
   216  			name: "init$guard",
   217  			typ:  types.NewPointer(tBool),
   218  		}
   219  		p.Members[initguard.Name()] = initguard
   220  	}
   221  
   222  	if prog.mode&GlobalDebug != 0 {
   223  		p.SetDebugMode(true)
   224  	}
   225  
   226  	if prog.mode&PrintPackages != 0 {
   227  		printMu.Lock()
   228  		p.WriteTo(os.Stdout)
   229  		printMu.Unlock()
   230  	}
   231  
   232  	if importable {
   233  		prog.imported[p.Pkg.Path()] = p
   234  	}
   235  	prog.packages[p.Pkg] = p
   236  
   237  	return p
   238  }
   239  
   240  // printMu serializes printing of Packages/Functions to stdout.
   241  var printMu sync.Mutex
   242  
   243  // AllPackages returns a new slice containing all packages in the
   244  // program prog in unspecified order.
   245  //
   246  func (prog *Program) AllPackages() []*Package {
   247  	pkgs := make([]*Package, 0, len(prog.packages))
   248  	for _, pkg := range prog.packages {
   249  		pkgs = append(pkgs, pkg)
   250  	}
   251  	return pkgs
   252  }
   253  
   254  // ImportedPackage returns the importable Package whose PkgPath
   255  // is path, or nil if no such Package has been created.
   256  //
   257  // A parameter to CreatePackage determines whether a package should be
   258  // considered importable. For example, no import declaration can resolve
   259  // to the ad-hoc main package created by 'go build foo.go'.
   260  //
   261  // TODO(adonovan): rethink this function and the "importable" concept;
   262  // most packages are importable. This function assumes that all
   263  // types.Package.Path values are unique within the ssa.Program, which is
   264  // false---yet this function remains very convenient.
   265  // Clients should use (*Program).Package instead where possible.
   266  // SSA doesn't really need a string-keyed map of packages.
   267  //
   268  func (prog *Program) ImportedPackage(path string) *Package {
   269  	return prog.imported[path]
   270  }