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