Home | History | Annotate | Download | only in cgo
      1 // Copyright 2011 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 package main
      6 
      7 import (
      8 	"bytes"
      9 	"fmt"
     10 	"go/ast"
     11 	"go/printer"
     12 	"go/token"
     13 	"os"
     14 	"path/filepath"
     15 	"strings"
     16 )
     17 
     18 // godefs returns the output for -godefs mode.
     19 func (p *Package) godefs(f *File, srcfile string) string {
     20 	var buf bytes.Buffer
     21 
     22 	fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n")
     23 	fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " "))
     24 	fmt.Fprintf(&buf, "\n")
     25 
     26 	override := make(map[string]string)
     27 
     28 	// Allow source file to specify override mappings.
     29 	// For example, the socket data structures refer
     30 	// to in_addr and in_addr6 structs but we want to be
     31 	// able to treat them as byte arrays, so the godefs
     32 	// inputs in package syscall say
     33 	//
     34 	//	// +godefs map struct_in_addr [4]byte
     35 	//	// +godefs map struct_in_addr6 [16]byte
     36 	//
     37 	for _, g := range f.Comments {
     38 		for _, c := range g.List {
     39 			i := strings.Index(c.Text, "+godefs map")
     40 			if i < 0 {
     41 				continue
     42 			}
     43 			s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
     44 			i = strings.Index(s, " ")
     45 			if i < 0 {
     46 				fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
     47 				continue
     48 			}
     49 			override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
     50 		}
     51 	}
     52 	for _, n := range f.Name {
     53 		if s := override[n.Go]; s != "" {
     54 			override[n.Mangle] = s
     55 		}
     56 	}
     57 
     58 	// Otherwise, if the source file says type T C.whatever,
     59 	// use "T" as the mangling of C.whatever,
     60 	// except in the definition (handled at end of function).
     61 	refName := make(map[*ast.Expr]*Name)
     62 	for _, r := range f.Ref {
     63 		refName[r.Expr] = r.Name
     64 	}
     65 	for _, d := range f.AST.Decls {
     66 		d, ok := d.(*ast.GenDecl)
     67 		if !ok || d.Tok != token.TYPE {
     68 			continue
     69 		}
     70 		for _, s := range d.Specs {
     71 			s := s.(*ast.TypeSpec)
     72 			n := refName[&s.Type]
     73 			if n != nil && n.Mangle != "" {
     74 				override[n.Mangle] = s.Name.Name
     75 			}
     76 		}
     77 	}
     78 
     79 	// Extend overrides using typedefs:
     80 	// If we know that C.xxx should format as T
     81 	// and xxx is a typedef for yyy, make C.yyy format as T.
     82 	for typ, def := range typedef {
     83 		if new := override[typ]; new != "" {
     84 			if id, ok := def.Go.(*ast.Ident); ok {
     85 				override[id.Name] = new
     86 			}
     87 		}
     88 	}
     89 
     90 	// Apply overrides.
     91 	for old, new := range override {
     92 		if id := goIdent[old]; id != nil {
     93 			id.Name = new
     94 		}
     95 	}
     96 
     97 	// Any names still using the _C syntax are not going to compile,
     98 	// although in general we don't know whether they all made it
     99 	// into the file, so we can't warn here.
    100 	//
    101 	// The most common case is union types, which begin with
    102 	// _Ctype_union and for which typedef[name] is a Go byte
    103 	// array of the appropriate size (such as [4]byte).
    104 	// Substitute those union types with byte arrays.
    105 	for name, id := range goIdent {
    106 		if id.Name == name && strings.Contains(name, "_Ctype_union") {
    107 			if def := typedef[name]; def != nil {
    108 				id.Name = gofmt(def)
    109 			}
    110 		}
    111 	}
    112 
    113 	conf.Fprint(&buf, fset, f.AST)
    114 
    115 	return buf.String()
    116 }
    117 
    118 var gofmtBuf bytes.Buffer
    119 
    120 // gofmt returns the gofmt-formatted string for an AST node.
    121 func gofmt(n interface{}) string {
    122 	gofmtBuf.Reset()
    123 	err := printer.Fprint(&gofmtBuf, fset, n)
    124 	if err != nil {
    125 		return "<" + err.Error() + ">"
    126 	}
    127 	return gofmtBuf.String()
    128 }
    129