...

Source file src/golang.org/x/tools/go/ssa/create.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 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  	"golang.org/x/tools/internal/versions"
    19  )
    20  
    21  // NewProgram returns a new SSA Program.
    22  //
    23  // mode controls diagnostics and checking during SSA construction.
    24  //
    25  // To construct an SSA program:
    26  //
    27  //   - Call NewProgram to create an empty Program.
    28  //   - Call CreatePackage providing typed syntax for each package
    29  //     you want to build, and call it with types but not
    30  //     syntax for each of those package's direct dependencies.
    31  //   - Call [Package.Build] on each syntax package you wish to build,
    32  //     or [Program.Build] to build all of them.
    33  //
    34  // See the Example tests for simple examples.
    35  func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
    36  	return &Program{
    37  		Fset:     fset,
    38  		imported: make(map[string]*Package),
    39  		packages: make(map[*types.Package]*Package),
    40  		mode:     mode,
    41  		canon:    newCanonizer(),
    42  		ctxt:     types.NewContext(),
    43  	}
    44  }
    45  
    46  // memberFromObject populates package pkg with a member for the
    47  // typechecker object obj.
    48  //
    49  // For objects from Go source code, syntax is the associated syntax
    50  // tree (for funcs and vars only) and goversion defines the
    51  // appropriate interpretation; they will be used during the build
    52  // phase.
    53  func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) {
    54  	name := obj.Name()
    55  	switch obj := obj.(type) {
    56  	case *types.Builtin:
    57  		if pkg.Pkg != types.Unsafe {
    58  			panic("unexpected builtin object: " + obj.String())
    59  		}
    60  
    61  	case *types.TypeName:
    62  		if name != "_" {
    63  			pkg.Members[name] = &Type{
    64  				object: obj,
    65  				pkg:    pkg,
    66  			}
    67  		}
    68  
    69  	case *types.Const:
    70  		c := &NamedConst{
    71  			object: obj,
    72  			Value:  NewConst(obj.Val(), obj.Type()),
    73  			pkg:    pkg,
    74  		}
    75  		pkg.objects[obj] = c
    76  		if name != "_" {
    77  			pkg.Members[name] = c
    78  		}
    79  
    80  	case *types.Var:
    81  		g := &Global{
    82  			Pkg:    pkg,
    83  			name:   name,
    84  			object: obj,
    85  			typ:    types.NewPointer(obj.Type()), // address
    86  			pos:    obj.Pos(),
    87  		}
    88  		pkg.objects[obj] = g
    89  		if name != "_" {
    90  			pkg.Members[name] = g
    91  		}
    92  
    93  	case *types.Func:
    94  		sig := obj.Type().(*types.Signature)
    95  		if sig.Recv() == nil && name == "init" {
    96  			pkg.ninit++
    97  			name = fmt.Sprintf("init#%d", pkg.ninit)
    98  		}
    99  		fn := createFunction(pkg.Prog, obj, name, syntax, pkg.info, goversion, &pkg.created)
   100  		fn.Pkg = pkg
   101  		pkg.objects[obj] = fn
   102  		if name != "_" && sig.Recv() == nil {
   103  			pkg.Members[name] = fn // package-level function
   104  		}
   105  
   106  	default: // (incl. *types.Package)
   107  		panic("unexpected Object type: " + obj.String())
   108  	}
   109  }
   110  
   111  // createFunction creates a function or method. It supports both
   112  // CreatePackage (with or without syntax) and the on-demand creation
   113  // of methods in non-created packages based on their types.Func.
   114  func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string, cr *creator) *Function {
   115  	sig := obj.Type().(*types.Signature)
   116  
   117  	// Collect type parameters.
   118  	var tparams *types.TypeParamList
   119  	if rtparams := sig.RecvTypeParams(); rtparams.Len() > 0 {
   120  		tparams = rtparams // method of generic type
   121  	} else if sigparams := sig.TypeParams(); sigparams.Len() > 0 {
   122  		tparams = sigparams // generic function
   123  	}
   124  
   125  	/* declared function/method (from syntax or export data) */
   126  	fn := &Function{
   127  		name:       name,
   128  		object:     obj,
   129  		Signature:  sig,
   130  		build:      (*builder).buildFromSyntax,
   131  		syntax:     syntax,
   132  		info:       info,
   133  		goversion:  goversion,
   134  		pos:        obj.Pos(),
   135  		Pkg:        nil, // may be set by caller
   136  		Prog:       prog,
   137  		typeparams: tparams,
   138  	}
   139  	if fn.syntax == nil {
   140  		fn.Synthetic = "from type information"
   141  		fn.build = (*builder).buildParamsOnly
   142  	}
   143  	if tparams.Len() > 0 {
   144  		fn.generic = new(generic)
   145  	}
   146  	cr.Add(fn)
   147  	return fn
   148  }
   149  
   150  // membersFromDecl populates package pkg with members for each
   151  // typechecker object (var, func, const or type) associated with the
   152  // specified decl.
   153  func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
   154  	switch decl := decl.(type) {
   155  	case *ast.GenDecl: // import, const, type or var
   156  		switch decl.Tok {
   157  		case token.CONST:
   158  			for _, spec := range decl.Specs {
   159  				for _, id := range spec.(*ast.ValueSpec).Names {
   160  					memberFromObject(pkg, pkg.info.Defs[id], nil, "")
   161  				}
   162  			}
   163  
   164  		case token.VAR:
   165  			for _, spec := range decl.Specs {
   166  				for _, rhs := range spec.(*ast.ValueSpec).Values {
   167  					pkg.initVersion[rhs] = goversion
   168  				}
   169  				for _, id := range spec.(*ast.ValueSpec).Names {
   170  					memberFromObject(pkg, pkg.info.Defs[id], spec, goversion)
   171  				}
   172  			}
   173  
   174  		case token.TYPE:
   175  			for _, spec := range decl.Specs {
   176  				id := spec.(*ast.TypeSpec).Name
   177  				memberFromObject(pkg, pkg.info.Defs[id], nil, "")
   178  			}
   179  		}
   180  
   181  	case *ast.FuncDecl:
   182  		id := decl.Name
   183  		memberFromObject(pkg, pkg.info.Defs[id], decl, goversion)
   184  	}
   185  }
   186  
   187  // creator tracks functions that have finished their CREATE phases.
   188  //
   189  // All Functions belong to the same Program. May have differing packages.
   190  //
   191  // creators are not thread-safe.
   192  type creator []*Function
   193  
   194  func (c *creator) Add(fn *Function) {
   195  	*c = append(*c, fn)
   196  }
   197  func (c *creator) At(i int) *Function { return (*c)[i] }
   198  func (c *creator) Len() int           { return len(*c) }
   199  
   200  // CreatePackage creates and returns an SSA Package from the
   201  // specified type-checked, error-free file ASTs, and populates its
   202  // Members mapping.
   203  //
   204  // importable determines whether this package should be returned by a
   205  // subsequent call to ImportedPackage(pkg.Path()).
   206  //
   207  // The real work of building SSA form for each function is not done
   208  // until a subsequent call to Package.Build.
   209  //
   210  // CreatePackage should not be called after building any package in
   211  // the program.
   212  func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
   213  	// TODO(adonovan): assert that no package has yet been built.
   214  	if pkg == nil {
   215  		panic("nil pkg") // otherwise pkg.Scope below returns types.Universe!
   216  	}
   217  	p := &Package{
   218  		Prog:    prog,
   219  		Members: make(map[string]Member),
   220  		objects: make(map[types.Object]Member),
   221  		Pkg:     pkg,
   222  		syntax:  info != nil,
   223  		// transient values (cleared after Package.Build)
   224  		info:        info,
   225  		files:       files,
   226  		initVersion: make(map[ast.Expr]string),
   227  	}
   228  
   229  	/* synthesized package initializer */
   230  	p.init = &Function{
   231  		name:      "init",
   232  		Signature: new(types.Signature),
   233  		Synthetic: "package initializer",
   234  		Pkg:       p,
   235  		Prog:      prog,
   236  		build:     (*builder).buildPackageInit,
   237  		info:      p.info,
   238  		goversion: "", // See Package.build for details.
   239  	}
   240  	p.Members[p.init.name] = p.init
   241  	p.created.Add(p.init)
   242  
   243  	// Allocate all package members: vars, funcs, consts and types.
   244  	if len(files) > 0 {
   245  		// Go source package.
   246  		for _, file := range files {
   247  			goversion := versions.Lang(versions.FileVersion(p.info, file))
   248  			for _, decl := range file.Decls {
   249  				membersFromDecl(p, decl, goversion)
   250  			}
   251  		}
   252  	} else {
   253  		// GC-compiled binary package (or "unsafe")
   254  		// No code.
   255  		// No position information.
   256  		scope := p.Pkg.Scope()
   257  		for _, name := range scope.Names() {
   258  			obj := scope.Lookup(name)
   259  			memberFromObject(p, obj, nil, "")
   260  			if obj, ok := obj.(*types.TypeName); ok {
   261  				// No Unalias: aliases should not duplicate methods.
   262  				if named, ok := obj.Type().(*types.Named); ok {
   263  					for i, n := 0, named.NumMethods(); i < n; i++ {
   264  						memberFromObject(p, named.Method(i), nil, "")
   265  					}
   266  				}
   267  			}
   268  		}
   269  	}
   270  
   271  	if prog.mode&BareInits == 0 {
   272  		// Add initializer guard variable.
   273  		initguard := &Global{
   274  			Pkg:  p,
   275  			name: "init$guard",
   276  			typ:  types.NewPointer(tBool),
   277  		}
   278  		p.Members[initguard.Name()] = initguard
   279  	}
   280  
   281  	if prog.mode&GlobalDebug != 0 {
   282  		p.SetDebugMode(true)
   283  	}
   284  
   285  	if prog.mode&PrintPackages != 0 {
   286  		printMu.Lock()
   287  		p.WriteTo(os.Stdout)
   288  		printMu.Unlock()
   289  	}
   290  
   291  	if importable {
   292  		prog.imported[p.Pkg.Path()] = p
   293  	}
   294  	prog.packages[p.Pkg] = p
   295  
   296  	return p
   297  }
   298  
   299  // printMu serializes printing of Packages/Functions to stdout.
   300  var printMu sync.Mutex
   301  
   302  // AllPackages returns a new slice containing all packages created by
   303  // prog.CreatePackage in unspecified order.
   304  func (prog *Program) AllPackages() []*Package {
   305  	pkgs := make([]*Package, 0, len(prog.packages))
   306  	for _, pkg := range prog.packages {
   307  		pkgs = append(pkgs, pkg)
   308  	}
   309  	return pkgs
   310  }
   311  
   312  // ImportedPackage returns the importable Package whose PkgPath
   313  // is path, or nil if no such Package has been created.
   314  //
   315  // A parameter to CreatePackage determines whether a package should be
   316  // considered importable. For example, no import declaration can resolve
   317  // to the ad-hoc main package created by 'go build foo.go'.
   318  //
   319  // TODO(adonovan): rethink this function and the "importable" concept;
   320  // most packages are importable. This function assumes that all
   321  // types.Package.Path values are unique within the ssa.Program, which is
   322  // false---yet this function remains very convenient.
   323  // Clients should use (*Program).Package instead where possible.
   324  // SSA doesn't really need a string-keyed map of packages.
   325  //
   326  // Furthermore, the graph of packages may contain multiple variants
   327  // (e.g. "p" vs "p as compiled for q.test"), and each has a different
   328  // view of its dependencies.
   329  func (prog *Program) ImportedPackage(path string) *Package {
   330  	return prog.imported[path]
   331  }
   332  

View as plain text