| // 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" |
| "strings" |
| ) |
| |
| // godefs returns the output for -godefs mode. |
| func (p *Package) godefs(f *File, srcfile string) string { |
| var buf bytes.Buffer |
| |
| fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n") |
| fmt.Fprintf(&buf, "// %s\n", strings.Join(os.Args, " ")) |
| 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() |
| } |
| |
| // cdefs returns the output for -cdefs mode. |
| // The easiest way to do this is to translate the godefs Go to C. |
| func (p *Package) cdefs(f *File, srcfile string) string { |
| godefsOutput := p.godefs(f, srcfile) |
| |
| lines := strings.Split(godefsOutput, "\n") |
| lines[0] = "// Created by cgo -cdefs - DO NOT EDIT" |
| |
| for i, line := range lines { |
| lines[i] = strings.TrimSpace(line) |
| } |
| |
| var out bytes.Buffer |
| printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) } |
| |
| didTypedef := false |
| for i := 0; i < len(lines); i++ { |
| line := lines[i] |
| |
| // Delete |
| // package x |
| if strings.HasPrefix(line, "package ") { |
| continue |
| } |
| |
| // Convert |
| // const ( |
| // A = 1 |
| // B = 2 |
| // ) |
| // |
| // to |
| // |
| // enum { |
| // A = 1, |
| // B = 2, |
| // }; |
| if line == "const (" { |
| printf("enum {\n") |
| for i++; i < len(lines) && lines[i] != ")"; i++ { |
| line = lines[i] |
| if line != "" { |
| printf("\t%s,", line) |
| } |
| printf("\n") |
| } |
| printf("};\n") |
| continue |
| } |
| |
| // Convert |
| // const A = 1 |
| // to |
| // enum { A = 1 }; |
| if strings.HasPrefix(line, "const ") { |
| printf("enum { %s };\n", line[len("const "):]) |
| continue |
| } |
| |
| // On first type definition, typedef all the structs |
| // in case there are dependencies between them. |
| if !didTypedef && strings.HasPrefix(line, "type ") { |
| didTypedef = true |
| for _, line := range lines { |
| line = strings.TrimSpace(line) |
| if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { |
| s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {") |
| printf("typedef struct %s %s;\n", s, s) |
| } |
| } |
| printf("\n") |
| printf("#pragma pack on\n") |
| printf("\n") |
| } |
| |
| // Convert |
| // type T struct { |
| // X int64 |
| // Y *int32 |
| // Z [4]byte |
| // } |
| // |
| // to |
| // |
| // struct T { |
| // int64 X; |
| // int32 *Y; |
| // byte Z[4]; |
| // } |
| if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { |
| if len(lines) > i+1 && lines[i+1] == "}" { |
| // do not output empty struct |
| i++ |
| continue |
| } |
| s := line[len("type ") : len(line)-len(" struct {")] |
| printf("struct %s {\n", s) |
| for i++; i < len(lines) && lines[i] != "}"; i++ { |
| line := lines[i] |
| if line != "" { |
| f := strings.Fields(line) |
| if len(f) != 2 { |
| fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line) |
| nerrors++ |
| continue |
| } |
| printf("\t%s;", cdecl(f[0], f[1])) |
| } |
| printf("\n") |
| } |
| printf("};\n") |
| continue |
| } |
| |
| // Convert |
| // type T int |
| // to |
| // typedef int T; |
| if strings.HasPrefix(line, "type ") { |
| f := strings.Fields(line[len("type "):]) |
| if len(f) != 2 { |
| fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line) |
| nerrors++ |
| continue |
| } |
| printf("typedef\t%s;\n", cdecl(f[0], f[1])) |
| continue |
| } |
| |
| printf("%s\n", line) |
| } |
| |
| if didTypedef { |
| printf("\n") |
| printf("#pragma pack off\n") |
| } |
| |
| return out.String() |
| } |
| |
| // cdecl returns the C declaration for the given Go name and type. |
| // It only handles the specific cases necessary for converting godefs output. |
| func cdecl(name, typ string) string { |
| // X *[0]byte -> X *void |
| if strings.HasPrefix(typ, "*[0]") { |
| typ = "*void" |
| } |
| // X [4]byte -> X[4] byte |
| for strings.HasPrefix(typ, "[") { |
| i := strings.Index(typ, "]") + 1 |
| name = name + typ[:i] |
| typ = typ[i:] |
| } |
| // X *byte -> *X byte |
| for strings.HasPrefix(typ, "*") { |
| name = "*" + name |
| typ = typ[1:] |
| } |
| // X T -> T X |
| // Handle the special case: 'unsafe.Pointer' is 'void *' |
| if typ == "unsafe.Pointer" { |
| typ = "void" |
| name = "*" + name |
| } |
| return typ + "\t" + name |
| } |
| |
| 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() |
| } |