Source file src/cmd/compile/internal/ir/mknode.go

     1  // Copyright 2020 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  //go:build ignore
     6  // +build ignore
     7  
     8  package main
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"go/format"
    14  	"go/types"
    15  	"io/ioutil"
    16  	"log"
    17  	"reflect"
    18  	"sort"
    19  	"strings"
    20  
    21  	"golang.org/x/tools/go/packages"
    22  )
    23  
    24  var irPkg *types.Package
    25  var buf bytes.Buffer
    26  
    27  func main() {
    28  	cfg := &packages.Config{
    29  		Mode: packages.NeedSyntax | packages.NeedTypes,
    30  	}
    31  	pkgs, err := packages.Load(cfg, "cmd/compile/internal/ir")
    32  	if err != nil {
    33  		log.Fatal(err)
    34  	}
    35  	irPkg = pkgs[0].Types
    36  
    37  	fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.")
    38  	fmt.Fprintln(&buf)
    39  	fmt.Fprintln(&buf, "package ir")
    40  	fmt.Fprintln(&buf)
    41  	fmt.Fprintln(&buf, `import "fmt"`)
    42  
    43  	scope := irPkg.Scope()
    44  	for _, name := range scope.Names() {
    45  		if strings.HasPrefix(name, "mini") {
    46  			continue
    47  		}
    48  
    49  		obj, ok := scope.Lookup(name).(*types.TypeName)
    50  		if !ok {
    51  			continue
    52  		}
    53  		typ := obj.Type().(*types.Named)
    54  		if !implementsNode(types.NewPointer(typ)) {
    55  			continue
    56  		}
    57  
    58  		fmt.Fprintf(&buf, "\n")
    59  		fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name)
    60  
    61  		switch name {
    62  		case "Name", "Func":
    63  			// Too specialized to automate.
    64  			continue
    65  		}
    66  
    67  		forNodeFields(typ,
    68  			"func (n *%[1]s) copy() Node { c := *n\n",
    69  			"",
    70  			"c.%[1]s = copy%[2]s(c.%[1]s)",
    71  			"return &c }\n")
    72  
    73  		forNodeFields(typ,
    74  			"func (n *%[1]s) doChildren(do func(Node) bool) bool {\n",
    75  			"if n.%[1]s != nil && do(n.%[1]s) { return true }",
    76  			"if do%[2]s(n.%[1]s, do) { return true }",
    77  			"return false }\n")
    78  
    79  		forNodeFields(typ,
    80  			"func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
    81  			"if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }",
    82  			"edit%[2]s(n.%[1]s, edit)",
    83  			"}\n")
    84  	}
    85  
    86  	makeHelpers()
    87  
    88  	out, err := format.Source(buf.Bytes())
    89  	if err != nil {
    90  		// write out mangled source so we can see the bug.
    91  		out = buf.Bytes()
    92  	}
    93  
    94  	err = ioutil.WriteFile("node_gen.go", out, 0666)
    95  	if err != nil {
    96  		log.Fatal(err)
    97  	}
    98  }
    99  
   100  // needHelper maps needed slice helpers from their base name to their
   101  // respective slice-element type.
   102  var needHelper = map[string]string{}
   103  
   104  func makeHelpers() {
   105  	var names []string
   106  	for name := range needHelper {
   107  		names = append(names, name)
   108  	}
   109  	sort.Strings(names)
   110  
   111  	for _, name := range names {
   112  		fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name])
   113  	}
   114  }
   115  
   116  const sliceHelperTmpl = `
   117  func copy%[1]s(list []%[2]s) []%[2]s {
   118  	if list == nil {
   119  		return nil
   120  	}
   121  	c := make([]%[2]s, len(list))
   122  	copy(c, list)
   123  	return c
   124  }
   125  func do%[1]s(list []%[2]s, do func(Node) bool) bool {
   126  	for _, x := range list {
   127  		if x != nil && do(x) {
   128  			return true
   129  		}
   130  	}
   131  	return false
   132  }
   133  func edit%[1]s(list []%[2]s, edit func(Node) Node) {
   134  	for i, x := range list {
   135  		if x != nil {
   136  			list[i] = edit(x).(%[2]s)
   137  		}
   138  	}
   139  }
   140  `
   141  
   142  func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) {
   143  	fmt.Fprintf(&buf, prologue, named.Obj().Name())
   144  
   145  	anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool {
   146  		if f.Embedded() {
   147  			return false
   148  		}
   149  		name, typ := f.Name(), f.Type()
   150  
   151  		slice, _ := typ.Underlying().(*types.Slice)
   152  		if slice != nil {
   153  			typ = slice.Elem()
   154  		}
   155  
   156  		tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg))
   157  		if implementsNode(typ) {
   158  			if slice != nil {
   159  				helper := strings.TrimPrefix(what, "*") + "s"
   160  				needHelper[helper] = what
   161  				tmpl, what = sliceTmpl, helper
   162  			}
   163  		} else if what == "*Field" {
   164  			// Special case for *Field.
   165  			tmpl = sliceTmpl
   166  			if slice != nil {
   167  				what = "Fields"
   168  			} else {
   169  				what = "Field"
   170  			}
   171  		} else {
   172  			return false
   173  		}
   174  
   175  		if tmpl == "" {
   176  			return false
   177  		}
   178  
   179  		// Allow template to not use all arguments without
   180  		// upsetting fmt.Printf.
   181  		s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what)
   182  		fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")])
   183  		return false
   184  	})
   185  
   186  	fmt.Fprintf(&buf, epilogue)
   187  }
   188  
   189  func implementsNode(typ types.Type) bool {
   190  	if _, ok := typ.Underlying().(*types.Interface); ok {
   191  		// TODO(mdempsky): Check the interface implements Node.
   192  		// Worst case, node_gen.go will fail to compile if we're wrong.
   193  		return true
   194  	}
   195  
   196  	if ptr, ok := typ.(*types.Pointer); ok {
   197  		if str, ok := ptr.Elem().Underlying().(*types.Struct); ok {
   198  			return anyField(str, func(f *types.Var) bool {
   199  				return f.Embedded() && f.Name() == "miniNode"
   200  			})
   201  		}
   202  	}
   203  
   204  	return false
   205  }
   206  
   207  func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool {
   208  	for i, n := 0, typ.NumFields(); i < n; i++ {
   209  		if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok {
   210  			if value != "-" {
   211  				panic(fmt.Sprintf("unexpected tag value: %q", value))
   212  			}
   213  			continue
   214  		}
   215  
   216  		f := typ.Field(i)
   217  		if pred(f) {
   218  			return true
   219  		}
   220  		if f.Embedded() {
   221  			if typ, ok := f.Type().Underlying().(*types.Struct); ok {
   222  				if anyField(typ, pred) {
   223  					return true
   224  				}
   225  			}
   226  		}
   227  	}
   228  	return false
   229  }
   230  

View as plain text