github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/src/cmd/cgo/godefs.go (about)

     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  	"strings"
    15  )
    16  
    17  // godefs returns the output for -godefs mode.
    18  func (p *Package) godefs(f *File, srcfile string) string {
    19  	var buf bytes.Buffer
    20  
    21  	fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n")
    22  	fmt.Fprintf(&buf, "// %s\n", strings.Join(os.Args, " "))
    23  	fmt.Fprintf(&buf, "\n")
    24  
    25  	override := make(map[string]string)
    26  
    27  	// Allow source file to specify override mappings.
    28  	// For example, the socket data structures refer
    29  	// to in_addr and in_addr6 structs but we want to be
    30  	// able to treat them as byte arrays, so the godefs
    31  	// inputs in package syscall say
    32  	//
    33  	//	// +godefs map struct_in_addr [4]byte
    34  	//	// +godefs map struct_in_addr6 [16]byte
    35  	//
    36  	for _, g := range f.Comments {
    37  		for _, c := range g.List {
    38  			i := strings.Index(c.Text, "+godefs map")
    39  			if i < 0 {
    40  				continue
    41  			}
    42  			s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
    43  			i = strings.Index(s, " ")
    44  			if i < 0 {
    45  				fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
    46  				continue
    47  			}
    48  			override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
    49  		}
    50  	}
    51  	for _, n := range f.Name {
    52  		if s := override[n.Go]; s != "" {
    53  			override[n.Mangle] = s
    54  		}
    55  	}
    56  
    57  	// Otherwise, if the source file says type T C.whatever,
    58  	// use "T" as the mangling of C.whatever,
    59  	// except in the definition (handled at end of function).
    60  	refName := make(map[*ast.Expr]*Name)
    61  	for _, r := range f.Ref {
    62  		refName[r.Expr] = r.Name
    63  	}
    64  	for _, d := range f.AST.Decls {
    65  		d, ok := d.(*ast.GenDecl)
    66  		if !ok || d.Tok != token.TYPE {
    67  			continue
    68  		}
    69  		for _, s := range d.Specs {
    70  			s := s.(*ast.TypeSpec)
    71  			n := refName[&s.Type]
    72  			if n != nil && n.Mangle != "" {
    73  				override[n.Mangle] = s.Name.Name
    74  			}
    75  		}
    76  	}
    77  
    78  	// Extend overrides using typedefs:
    79  	// If we know that C.xxx should format as T
    80  	// and xxx is a typedef for yyy, make C.yyy format as T.
    81  	for typ, def := range typedef {
    82  		if new := override[typ]; new != "" {
    83  			if id, ok := def.Go.(*ast.Ident); ok {
    84  				override[id.Name] = new
    85  			}
    86  		}
    87  	}
    88  
    89  	// Apply overrides.
    90  	for old, new := range override {
    91  		if id := goIdent[old]; id != nil {
    92  			id.Name = new
    93  		}
    94  	}
    95  
    96  	// Any names still using the _C syntax are not going to compile,
    97  	// although in general we don't know whether they all made it
    98  	// into the file, so we can't warn here.
    99  	//
   100  	// The most common case is union types, which begin with
   101  	// _Ctype_union and for which typedef[name] is a Go byte
   102  	// array of the appropriate size (such as [4]byte).
   103  	// Substitute those union types with byte arrays.
   104  	for name, id := range goIdent {
   105  		if id.Name == name && strings.Contains(name, "_Ctype_union") {
   106  			if def := typedef[name]; def != nil {
   107  				id.Name = gofmt(def)
   108  			}
   109  		}
   110  	}
   111  
   112  	conf.Fprint(&buf, fset, f.AST)
   113  
   114  	return buf.String()
   115  }
   116  
   117  // cdefs returns the output for -cdefs mode.
   118  // The easiest way to do this is to translate the godefs Go to C.
   119  func (p *Package) cdefs(f *File, srcfile string) string {
   120  	godefsOutput := p.godefs(f, srcfile)
   121  
   122  	lines := strings.Split(godefsOutput, "\n")
   123  	lines[0] = "// Created by cgo -cdefs - DO NOT EDIT"
   124  
   125  	for i, line := range lines {
   126  		lines[i] = strings.TrimSpace(line)
   127  	}
   128  
   129  	var out bytes.Buffer
   130  	printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) }
   131  
   132  	didTypedef := false
   133  	for i := 0; i < len(lines); i++ {
   134  		line := lines[i]
   135  
   136  		// Delete
   137  		//	package x
   138  		if strings.HasPrefix(line, "package ") {
   139  			continue
   140  		}
   141  
   142  		// Convert
   143  		//	const (
   144  		//		A = 1
   145  		//		B = 2
   146  		//	)
   147  		//
   148  		// to
   149  		//
   150  		//	enum {
   151  		//		A = 1,
   152  		//		B = 2,
   153  		//	};
   154  		if line == "const (" {
   155  			printf("enum {\n")
   156  			for i++; i < len(lines) && lines[i] != ")"; i++ {
   157  				line = lines[i]
   158  				if line != "" {
   159  					printf("\t%s,", line)
   160  				}
   161  				printf("\n")
   162  			}
   163  			printf("};\n")
   164  			continue
   165  		}
   166  
   167  		// Convert
   168  		//	const A = 1
   169  		// to
   170  		//	enum { A = 1 };
   171  		if strings.HasPrefix(line, "const ") {
   172  			printf("enum { %s };\n", line[len("const "):])
   173  			continue
   174  		}
   175  
   176  		// On first type definition, typedef all the structs
   177  		// in case there are dependencies between them.
   178  		if !didTypedef && strings.HasPrefix(line, "type ") {
   179  			didTypedef = true
   180  			for _, line := range lines {
   181  				line = strings.TrimSpace(line)
   182  				if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
   183  					s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {")
   184  					printf("typedef struct %s %s;\n", s, s)
   185  				}
   186  			}
   187  			printf("\n")
   188  			printf("#pragma pack on\n")
   189  			printf("\n")
   190  		}
   191  
   192  		// Convert
   193  		//	type T struct {
   194  		//		X int64
   195  		//		Y *int32
   196  		//		Z [4]byte
   197  		//	}
   198  		//
   199  		// to
   200  		//
   201  		//	struct T {
   202  		//		int64 X;
   203  		//		int32 *Y;
   204  		//		byte Z[4];
   205  		//	}
   206  		if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
   207  			if len(lines) > i+1 && lines[i+1] == "}" {
   208  				// do not output empty struct
   209  				i++
   210  				continue
   211  			}
   212  			s := line[len("type ") : len(line)-len(" struct {")]
   213  			printf("struct %s {\n", s)
   214  			for i++; i < len(lines) && lines[i] != "}"; i++ {
   215  				line := lines[i]
   216  				if line != "" {
   217  					f := strings.Fields(line)
   218  					if len(f) != 2 {
   219  						fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line)
   220  						nerrors++
   221  						continue
   222  					}
   223  					printf("\t%s;", cdecl(f[0], f[1]))
   224  				}
   225  				printf("\n")
   226  			}
   227  			printf("};\n")
   228  			continue
   229  		}
   230  
   231  		// Convert
   232  		//	type T int
   233  		// to
   234  		//	typedef int T;
   235  		if strings.HasPrefix(line, "type ") {
   236  			f := strings.Fields(line[len("type "):])
   237  			if len(f) != 2 {
   238  				fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line)
   239  				nerrors++
   240  				continue
   241  			}
   242  			printf("typedef\t%s;\n", cdecl(f[0], f[1]))
   243  			continue
   244  		}
   245  
   246  		printf("%s\n", line)
   247  	}
   248  
   249  	if didTypedef {
   250  		printf("\n")
   251  		printf("#pragma pack off\n")
   252  	}
   253  
   254  	return out.String()
   255  }
   256  
   257  // cdecl returns the C declaration for the given Go name and type.
   258  // It only handles the specific cases necessary for converting godefs output.
   259  func cdecl(name, typ string) string {
   260  	// X *[0]byte -> X *void
   261  	if strings.HasPrefix(typ, "*[0]") {
   262  		typ = "*void"
   263  	}
   264  	// X [4]byte -> X[4] byte
   265  	for strings.HasPrefix(typ, "[") {
   266  		i := strings.Index(typ, "]") + 1
   267  		name = name + typ[:i]
   268  		typ = typ[i:]
   269  	}
   270  	// X *byte -> *X byte
   271  	for strings.HasPrefix(typ, "*") {
   272  		name = "*" + name
   273  		typ = typ[1:]
   274  	}
   275  	// X T -> T X
   276  	// Handle the special case: 'unsafe.Pointer' is 'void *'
   277  	if typ == "unsafe.Pointer" {
   278  		typ = "void"
   279  		name = "*" + name
   280  	}
   281  	return typ + "\t" + name
   282  }
   283  
   284  var gofmtBuf bytes.Buffer
   285  
   286  // gofmt returns the gofmt-formatted string for an AST node.
   287  func gofmt(n interface{}) string {
   288  	gofmtBuf.Reset()
   289  	err := printer.Fprint(&gofmtBuf, fset, n)
   290  	if err != nil {
   291  		return "<" + err.Error() + ">"
   292  	}
   293  	return gofmtBuf.String()
   294  }