Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/cgo/godefs.go

Documentation: cmd/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) string {
    20  	var buf bytes.Buffer
    21  
    22  	fmt.Fprintf(&buf, "// Code generated by cmd/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  
   130  // gofmtLineReplacer is used to put a gofmt-formatted string for an
   131  // AST expression onto a single line. The lexer normally inserts a
   132  // semicolon at each newline, so we can replace newline with semicolon.
   133  // However, we can't do that in cases where the lexer would not insert
   134  // a semicolon. We only have to worry about cases that can occur in an
   135  // expression passed through gofmt, which means composite literals and
   136  // (due to the printer possibly inserting newlines because of position
   137  // information) operators.
   138  var gofmtLineReplacer = strings.NewReplacer(
   139  	// Want to replace \n without ; after everything from
   140  	// https://golang.org/ref/spec#Operators_and_punctuation
   141  	// EXCEPT ++ -- ) ] }
   142  	"++\n", "++;",
   143  	"--\n", "--;",
   144  
   145  	"+\n", "+ ",
   146  	"-\n", "- ",
   147  	"*\n", "* ",
   148  	"/\n", "/ ",
   149  	"%\n", "% ",
   150  	"&\n", "& ",
   151  	"|\n", "| ",
   152  	"^\n", "^ ",
   153  	"<\n", "< ",
   154  	">\n", "> ",
   155  	"=\n", "= ",
   156  	"!\n", "! ", // not possible in gofmt today
   157  	"(\n", "(",
   158  	"[\n", "[", // not possible in gofmt today
   159  	"{\n", "{",
   160  	",\n", ",",
   161  	".\n", ". ",
   162  	":\n", ": ", // not possible in gofmt today
   163  
   164  	"\n", ";",
   165  )
   166  
   167  // gofmtLine returns the gofmt-formatted string for an AST node,
   168  // ensuring that it is on a single line.
   169  func gofmtLine(n interface{}) string {
   170  	return gofmtLineReplacer.Replace(gofmt(n))
   171  }
   172  

View as plain text