| // Copyright 2011 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. |
| |
| package main |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/ast" |
| "go/printer" |
| "go/token" |
| "os" |
| "path/filepath" |
| "strings" |
| ) |
| |
| // godefs returns the output for -godefs mode. |
| func (p *Package) godefs(f *File, args []string) string { |
| var buf bytes.Buffer |
| |
| fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n") |
| fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(args[0]), strings.Join(args[1:], " ")) |
| fmt.Fprintf(&buf, "\n") |
| |
| override := make(map[string]string) |
| |
| // Allow source file to specify override mappings. |
| // For example, the socket data structures refer |
| // to in_addr and in_addr6 structs but we want to be |
| // able to treat them as byte arrays, so the godefs |
| // inputs in package syscall say |
| // |
| // // +godefs map struct_in_addr [4]byte |
| // // +godefs map struct_in_addr6 [16]byte |
| // |
| for _, g := range f.Comments { |
| for _, c := range g.List { |
| i := strings.Index(c.Text, "+godefs map") |
| if i < 0 { |
| continue |
| } |
| s := strings.TrimSpace(c.Text[i+len("+godefs map"):]) |
| i = strings.Index(s, " ") |
| if i < 0 { |
| fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text) |
| continue |
| } |
| override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:]) |
| } |
| } |
| for _, n := range f.Name { |
| if s := override[n.Go]; s != "" { |
| override[n.Mangle] = s |
| } |
| } |
| |
| // Otherwise, if the source file says type T C.whatever, |
| // use "T" as the mangling of C.whatever, |
| // except in the definition (handled at end of function). |
| refName := make(map[*ast.Expr]*Name) |
| for _, r := range f.Ref { |
| refName[r.Expr] = r.Name |
| } |
| for _, d := range f.AST.Decls { |
| d, ok := d.(*ast.GenDecl) |
| if !ok || d.Tok != token.TYPE { |
| continue |
| } |
| for _, s := range d.Specs { |
| s := s.(*ast.TypeSpec) |
| n := refName[&s.Type] |
| if n != nil && n.Mangle != "" { |
| override[n.Mangle] = s.Name.Name |
| } |
| } |
| } |
| |
| // Extend overrides using typedefs: |
| // If we know that C.xxx should format as T |
| // and xxx is a typedef for yyy, make C.yyy format as T. |
| for typ, def := range typedef { |
| if new := override[typ]; new != "" { |
| if id, ok := def.Go.(*ast.Ident); ok { |
| override[id.Name] = new |
| } |
| } |
| } |
| |
| // Apply overrides. |
| for old, new := range override { |
| if id := goIdent[old]; id != nil { |
| id.Name = new |
| } |
| } |
| |
| // Any names still using the _C syntax are not going to compile, |
| // although in general we don't know whether they all made it |
| // into the file, so we can't warn here. |
| // |
| // The most common case is union types, which begin with |
| // _Ctype_union and for which typedef[name] is a Go byte |
| // array of the appropriate size (such as [4]byte). |
| // Substitute those union types with byte arrays. |
| for name, id := range goIdent { |
| if id.Name == name && strings.Contains(name, "_Ctype_union") { |
| if def := typedef[name]; def != nil { |
| id.Name = gofmt(def) |
| } |
| } |
| } |
| |
| conf.Fprint(&buf, fset, f.AST) |
| |
| return buf.String() |
| } |
| |
| var gofmtBuf bytes.Buffer |
| |
| // gofmt returns the gofmt-formatted string for an AST node. |
| func gofmt(n interface{}) string { |
| gofmtBuf.Reset() |
| err := printer.Fprint(&gofmtBuf, fset, n) |
| if err != nil { |
| return "<" + err.Error() + ">" |
| } |
| return gofmtBuf.String() |
| } |
| |
| // gofmtLineReplacer is used to put a gofmt-formatted string for an |
| // AST expression onto a single line. The lexer normally inserts a |
| // semicolon at each newline, so we can replace newline with semicolon. |
| // However, we can't do that in cases where the lexer would not insert |
| // a semicolon. We only have to worry about cases that can occur in an |
| // expression passed through gofmt, which means composite literals and |
| // (due to the printer possibly inserting newlines because of position |
| // information) operators. |
| var gofmtLineReplacer = strings.NewReplacer( |
| // Want to replace \n without ; after everything from |
| // https://golang.org/ref/spec#Operators_and_punctuation |
| // EXCEPT ++ -- ) ] } |
| "++\n", "++;", |
| "--\n", "--;", |
| |
| "+\n", "+ ", |
| "-\n", "- ", |
| "*\n", "* ", |
| "/\n", "/ ", |
| "%\n", "% ", |
| "&\n", "& ", |
| "|\n", "| ", |
| "^\n", "^ ", |
| "<\n", "< ", |
| ">\n", "> ", |
| "=\n", "= ", |
| "!\n", "! ", // not possible in gofmt today |
| "(\n", "(", |
| "[\n", "[", // not possible in gofmt today |
| "{\n", "{", |
| ",\n", ",", |
| ".\n", ". ", |
| ":\n", ": ", // not possible in gofmt today |
| |
| "\n", ";", |
| ) |
| |
| // gofmtLine returns the gofmt-formatted string for an AST node, |
| // ensuring that it is on a single line. |
| func gofmtLine(n interface{}) string { |
| return gofmtLineReplacer.Replace(gofmt(n)) |
| } |