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 }