github.com/bir3/gocompiler@v0.9.2202/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 cgo 6 7 import ( 8 "fmt" 9 "github.com/bir3/gocompiler/src/go/ast" 10 "github.com/bir3/gocompiler/src/go/printer" 11 "github.com/bir3/gocompiler/src/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 }