123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- // 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()
- }
|