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  }