Source file src/cmd/cgo/godefs.go

     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  	"fmt"
     9  	"go/ast"
    10  	"go/printer"
    11  	"go/token"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  )
    16  
    17  // godefs returns the output for -godefs mode.
    18  func (p *Package) godefs(f *File, args []string) string {
    19  	var buf strings.Builder
    20  
    21  	fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n")
    22  	fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(args[0]), strings.Join(args[1:], " "))
    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  var gofmtBuf strings.Builder
   118  
   119  // gofmt returns the gofmt-formatted string for an AST node.
   120  func gofmt(n interface{}) string {
   121  	gofmtBuf.Reset()
   122  	err := printer.Fprint(&gofmtBuf, fset, n)
   123  	if err != nil {
   124  		return "<" + err.Error() + ">"
   125  	}
   126  	return gofmtBuf.String()
   127  }
   128  
   129  // gofmtLineReplacer is used to put a gofmt-formatted string for an
   130  // AST expression onto a single line. The lexer normally inserts a
   131  // semicolon at each newline, so we can replace newline with semicolon.
   132  // However, we can't do that in cases where the lexer would not insert
   133  // a semicolon. We only have to worry about cases that can occur in an
   134  // expression passed through gofmt, which means composite literals and
   135  // (due to the printer possibly inserting newlines because of position
   136  // information) operators.
   137  var gofmtLineReplacer = strings.NewReplacer(
   138  	// Want to replace \n without ; after everything from
   139  	// https://golang.org/ref/spec#Operators_and_punctuation
   140  	// EXCEPT ++ -- ) ] }
   141  	"++\n", "++;",
   142  	"--\n", "--;",
   143  
   144  	"+\n", "+ ",
   145  	"-\n", "- ",
   146  	"*\n", "* ",
   147  	"/\n", "/ ",
   148  	"%\n", "% ",
   149  	"&\n", "& ",
   150  	"|\n", "| ",
   151  	"^\n", "^ ",
   152  	"<\n", "< ",
   153  	">\n", "> ",
   154  	"=\n", "= ",
   155  	"!\n", "! ", // not possible in gofmt today
   156  	"(\n", "(",
   157  	"[\n", "[", // not possible in gofmt today
   158  	"{\n", "{",
   159  	",\n", ",",
   160  	".\n", ". ",
   161  	":\n", ": ", // not possible in gofmt today
   162  
   163  	"\n", ";",
   164  )
   165  
   166  // gofmtLine returns the gofmt-formatted string for an AST node,
   167  // ensuring that it is on a single line.
   168  func gofmtLine(n interface{}) string {
   169  	return gofmtLineReplacer.Replace(gofmt(n))
   170  }
   171  

View as plain text