github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/example_test.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_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/ast" 11 "go/importer" 12 "go/parser" 13 "go/token" 14 "go/types" 15 "log" 16 "os" 17 "strings" 18 19 "github.com/amarpal/go-tools/go/ir" 20 "github.com/amarpal/go-tools/go/ir/irutil" 21 22 "golang.org/x/tools/go/packages" 23 ) 24 25 const hello = ` 26 package main 27 28 import "fmt" 29 30 const message = "Hello, World!" 31 32 func main() { 33 fmt.Println(message) 34 } 35 ` 36 37 // This program demonstrates how to run the IR builder on a single 38 // package of one or more already-parsed files. Its dependencies are 39 // loaded from compiler export data. This is what you'd typically use 40 // for a compiler; it does not depend on golang.org/x/tools/go/loader. 41 // 42 // It shows the printed representation of packages, functions, and 43 // instructions. Within the function listing, the name of each 44 // BasicBlock such as ".0.entry" is printed left-aligned, followed by 45 // the block's Instructions. 46 // 47 // For each instruction that defines an IR virtual register 48 // (i.e. implements Value), the type of that value is shown in the 49 // right column. 50 // 51 // Build and run the irdump.go program if you want a standalone tool 52 // with similar functionality. It is located at 53 // github.com/amarpal/go-tools/internal/cmd/irdump. 54 func Example_buildPackage() { 55 // Parse the source files. 56 fset := token.NewFileSet() 57 f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments|parser.SkipObjectResolution) 58 if err != nil { 59 fmt.Print(err) // parse error 60 return 61 } 62 files := []*ast.File{f} 63 64 // Create the type-checker's package. 65 pkg := types.NewPackage("hello", "") 66 67 // Type-check the package, load dependencies. 68 // Create and build the IR program. 69 hello, _, err := irutil.BuildPackage( 70 &types.Config{Importer: importer.Default()}, fset, pkg, files, ir.SanityCheckFunctions) 71 if err != nil { 72 fmt.Print(err) // type error in some package 73 return 74 } 75 76 // Print out the package. 77 hello.WriteTo(os.Stdout) 78 79 // Print out the package-level functions. 80 // Replace interface{} with any so the tests work for Go 1.17 and Go 1.18. 81 { 82 var buf bytes.Buffer 83 ir.WriteFunction(&buf, hello.Func("init")) 84 fmt.Print(strings.ReplaceAll(buf.String(), "interface{}", "any")) 85 } 86 { 87 var buf bytes.Buffer 88 ir.WriteFunction(&buf, hello.Func("main")) 89 fmt.Print(strings.ReplaceAll(buf.String(), "interface{}", "any")) 90 } 91 92 // Output: 93 // package hello: 94 // func init func() 95 // var init$guard bool 96 // func main func() 97 // const message message = Const <untyped string> {"Hello, World!"} 98 // 99 // # Name: hello.init 100 // # Package: hello 101 // # Synthetic: package initializer 102 // func init(): 103 // b0: # entry 104 // t1 = Const <bool> {true} 105 // t2 = Load <bool> init$guard 106 // If t2 → b1 b2 107 // 108 // b1: ← b0 b2 # exit 109 // Return 110 // 111 // b2: ← b0 # init.start 112 // Store {bool} init$guard t1 113 // t6 = Call <()> fmt.init 114 // Jump → b1 115 // 116 // # Name: hello.main 117 // # Package: hello 118 // # Location: hello.go:8:1 119 // func main(): 120 // b0: # entry 121 // t1 = Const <string> {"Hello, World!"} 122 // t2 = Const <int> {0} 123 // t3 = HeapAlloc <*[1]any> 124 // t4 = IndexAddr <*any> t3 t2 125 // t5 = MakeInterface <any> t1 126 // Store {any} t4 t5 127 // t7 = Slice <[]any> t3 <nil> <nil> <nil> 128 // t8 = Call <(n int, err error)> fmt.Println t7 129 // Jump → b1 130 // 131 // b1: ← b0 # exit 132 // Return 133 } 134 135 // This example builds IR code for a set of packages using the 136 // x/tools/go/packages API. This is what you would typically use for a 137 // analysis capable of operating on a single package. 138 func Example_loadPackages() { 139 // Load, parse, and type-check the initial packages. 140 cfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo} 141 initial, err := packages.Load(cfg, "fmt", "net/http") 142 if err != nil { 143 log.Fatal(err) 144 } 145 146 // Stop if any package had errors. 147 // This step is optional; without it, the next step 148 // will create IR for only a subset of packages. 149 if packages.PrintErrors(initial) > 0 { 150 log.Fatalf("packages contain errors") 151 } 152 153 // Create IR packages for all well-typed packages. 154 prog, pkgs := irutil.Packages(initial, ir.PrintPackages, nil) 155 _ = prog 156 157 // Build IR code for the well-typed initial packages. 158 for _, p := range pkgs { 159 if p != nil { 160 p.Build() 161 } 162 } 163 } 164 165 // This example builds IR code for a set of packages plus all their dependencies, 166 // using the x/tools/go/packages API. 167 // This is what you'd typically use for a whole-program analysis. 168 func Example_loadWholeProgram() { 169 // Load, parse, and type-check the whole program. 170 cfg := packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps} 171 initial, err := packages.Load(&cfg, "fmt", "net/http") 172 if err != nil { 173 log.Fatal(err) 174 } 175 176 // Create IR packages for well-typed packages and their dependencies. 177 prog, pkgs := irutil.AllPackages(initial, ir.PrintPackages, nil) 178 _ = pkgs 179 180 // Build IR code for the whole program. 181 prog.Build() 182 }