| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build ignore |
| |
| // Generate builtin.go from builtin/runtime.go. |
| |
| package main |
| |
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "go/ast" |
| "go/format" |
| "go/parser" |
| "go/token" |
| "io" |
| "log" |
| "os" |
| "path/filepath" |
| "strconv" |
| "strings" |
| ) |
| |
| var stdout = flag.Bool("stdout", false, "write to stdout instead of builtin.go") |
| var nofmt = flag.Bool("nofmt", false, "skip formatting builtin.go") |
| |
| func main() { |
| flag.Parse() |
| |
| var b bytes.Buffer |
| fmt.Fprintln(&b, "// Code generated by mkbuiltin.go. DO NOT EDIT.") |
| fmt.Fprintln(&b) |
| fmt.Fprintln(&b, "package typecheck") |
| fmt.Fprintln(&b) |
| fmt.Fprintln(&b, `import (`) |
| fmt.Fprintln(&b, ` "cmd/compile/internal/types"`) |
| fmt.Fprintln(&b, ` "cmd/internal/src"`) |
| fmt.Fprintln(&b, `)`) |
| |
| fmt.Fprintln(&b, ` |
| // Not inlining this function removes a significant chunk of init code. |
| //go:noinline |
| func newSig(params, results []*types.Field) *types.Type { |
| return types.NewSignature(nil, params, results) |
| } |
| |
| func params(tlist ...*types.Type) []*types.Field { |
| flist := make([]*types.Field, len(tlist)) |
| for i, typ := range tlist { |
| flist[i] = types.NewField(src.NoXPos, nil, typ) |
| } |
| return flist |
| } |
| `) |
| |
| mkbuiltin(&b, "runtime") |
| mkbuiltin(&b, "coverage") |
| |
| var err error |
| out := b.Bytes() |
| if !*nofmt { |
| out, err = format.Source(out) |
| if err != nil { |
| log.Fatal(err) |
| } |
| } |
| if *stdout { |
| _, err = os.Stdout.Write(out) |
| } else { |
| err = os.WriteFile("builtin.go", out, 0666) |
| } |
| if err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| func mkbuiltin(w io.Writer, name string) { |
| fset := token.NewFileSet() |
| f, err := parser.ParseFile(fset, filepath.Join("_builtin", name+".go"), nil, 0) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| var interner typeInterner |
| |
| fmt.Fprintf(w, "var %sDecls = [...]struct { name string; tag int; typ int }{\n", name) |
| for _, decl := range f.Decls { |
| switch decl := decl.(type) { |
| case *ast.FuncDecl: |
| if decl.Recv != nil { |
| log.Fatal("methods unsupported") |
| } |
| if decl.Body != nil { |
| log.Fatal("unexpected function body") |
| } |
| fmt.Fprintf(w, "{%q, funcTag, %d},\n", decl.Name.Name, interner.intern(decl.Type)) |
| case *ast.GenDecl: |
| if decl.Tok == token.IMPORT { |
| if len(decl.Specs) != 1 || decl.Specs[0].(*ast.ImportSpec).Path.Value != "\"unsafe\"" { |
| log.Fatal("runtime cannot import other package") |
| } |
| continue |
| } |
| if decl.Tok != token.VAR { |
| log.Fatal("unhandled declaration kind", decl.Tok) |
| } |
| for _, spec := range decl.Specs { |
| spec := spec.(*ast.ValueSpec) |
| if len(spec.Values) != 0 { |
| log.Fatal("unexpected values") |
| } |
| typ := interner.intern(spec.Type) |
| for _, name := range spec.Names { |
| fmt.Fprintf(w, "{%q, varTag, %d},\n", name.Name, typ) |
| } |
| } |
| default: |
| log.Fatal("unhandled decl type", decl) |
| } |
| } |
| fmt.Fprintln(w, "}") |
| |
| fmt.Fprintln(w) |
| fmt.Fprintf(w, "func %sTypes() []*types.Type {\n", name) |
| fmt.Fprintf(w, "var typs [%d]*types.Type\n", len(interner.typs)) |
| for i, typ := range interner.typs { |
| fmt.Fprintf(w, "typs[%d] = %s\n", i, typ) |
| } |
| fmt.Fprintln(w, "return typs[:]") |
| fmt.Fprintln(w, "}") |
| } |
| |
| // typeInterner maps Go type expressions to compiler code that |
| // constructs the denoted type. It recognizes and reuses common |
| // subtype expressions. |
| type typeInterner struct { |
| typs []string |
| hash map[string]int |
| } |
| |
| func (i *typeInterner) intern(t ast.Expr) int { |
| x := i.mktype(t) |
| v, ok := i.hash[x] |
| if !ok { |
| v = len(i.typs) |
| if i.hash == nil { |
| i.hash = make(map[string]int) |
| } |
| i.hash[x] = v |
| i.typs = append(i.typs, x) |
| } |
| return v |
| } |
| |
| func (i *typeInterner) subtype(t ast.Expr) string { |
| return fmt.Sprintf("typs[%d]", i.intern(t)) |
| } |
| |
| func (i *typeInterner) mktype(t ast.Expr) string { |
| switch t := t.(type) { |
| case *ast.Ident: |
| switch t.Name { |
| case "byte": |
| return "types.ByteType" |
| case "rune": |
| return "types.RuneType" |
| } |
| return fmt.Sprintf("types.Types[types.T%s]", strings.ToUpper(t.Name)) |
| case *ast.SelectorExpr: |
| if t.X.(*ast.Ident).Name != "unsafe" || t.Sel.Name != "Pointer" { |
| log.Fatalf("unhandled type: %#v", t) |
| } |
| return "types.Types[types.TUNSAFEPTR]" |
| |
| case *ast.ArrayType: |
| if t.Len == nil { |
| return fmt.Sprintf("types.NewSlice(%s)", i.subtype(t.Elt)) |
| } |
| return fmt.Sprintf("types.NewArray(%s, %d)", i.subtype(t.Elt), intconst(t.Len)) |
| case *ast.ChanType: |
| dir := "types.Cboth" |
| switch t.Dir { |
| case ast.SEND: |
| dir = "types.Csend" |
| case ast.RECV: |
| dir = "types.Crecv" |
| } |
| return fmt.Sprintf("types.NewChan(%s, %s)", i.subtype(t.Value), dir) |
| case *ast.FuncType: |
| return fmt.Sprintf("newSig(%s, %s)", i.fields(t.Params, false), i.fields(t.Results, false)) |
| case *ast.InterfaceType: |
| if len(t.Methods.List) != 0 { |
| log.Fatal("non-empty interfaces unsupported") |
| } |
| return "types.Types[types.TINTER]" |
| case *ast.MapType: |
| return fmt.Sprintf("types.NewMap(%s, %s)", i.subtype(t.Key), i.subtype(t.Value)) |
| case *ast.StarExpr: |
| return fmt.Sprintf("types.NewPtr(%s)", i.subtype(t.X)) |
| case *ast.StructType: |
| return fmt.Sprintf("types.NewStruct(%s)", i.fields(t.Fields, true)) |
| |
| default: |
| log.Fatalf("unhandled type: %#v", t) |
| panic("unreachable") |
| } |
| } |
| |
| func (i *typeInterner) fields(fl *ast.FieldList, keepNames bool) string { |
| if fl == nil || len(fl.List) == 0 { |
| return "nil" |
| } |
| |
| var res []string |
| for _, f := range fl.List { |
| typ := i.subtype(f.Type) |
| if len(f.Names) == 0 { |
| res = append(res, typ) |
| } else { |
| for _, name := range f.Names { |
| if keepNames { |
| res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, Lookup(%q), %s)", name.Name, typ)) |
| } else { |
| res = append(res, typ) |
| } |
| } |
| } |
| } |
| |
| if keepNames { |
| return fmt.Sprintf("[]*types.Field{%s}", strings.Join(res, ", ")) |
| } |
| return fmt.Sprintf("params(%s)", strings.Join(res, ", ")) |
| } |
| |
| func intconst(e ast.Expr) int64 { |
| switch e := e.(type) { |
| case *ast.BasicLit: |
| if e.Kind != token.INT { |
| log.Fatalf("expected INT, got %v", e.Kind) |
| } |
| x, err := strconv.ParseInt(e.Value, 0, 64) |
| if err != nil { |
| log.Fatal(err) |
| } |
| return x |
| default: |
| log.Fatalf("unhandled expr: %#v", e) |
| panic("unreachable") |
| } |
| } |