1 // Package cgo implements CGo by modifying a loaded AST. It does this by parsing
2 // the `import "C"` statements found in the source code with libclang and
3 // generating stub function and global declarations.
4 //
5 // There are a few advantages to modifying the AST directly instead of doing CGo
6 // as a preprocessing step, with the main advantage being that debug information
7 // is kept intact as much as possible.
8 package cgo
9 10 // This file extracts the `import "C"` statement from the source and modifies
11 // the AST for CGo. It does not use libclang directly: see libclang.go for the C
12 // source file parsing.
13 14 import (
15 "fmt"
16 "go/ast"
17 "go/parser"
18 "go/scanner"
19 "go/token"
20 "path/filepath"
21 "sort"
22 "strconv"
23 "strings"
24 25 "github.com/google/shlex"
26 "golang.org/x/tools/go/ast/astutil"
27 )
28 29 // Function that's only defined in Go 1.22.
30 var setASTFileFields = func(f *ast.File, start, end token.Pos) {
31 }
32 33 // cgoPackage holds all CGo-related information of a package.
34 type cgoPackage struct {
35 generated *ast.File
36 packageName string
37 cgoFiles []*ast.File
38 generatedPos token.Pos
39 errors []error
40 currentDir string // current working directory
41 packageDir string // full path to the package to process
42 importPath string
43 fset *token.FileSet
44 tokenFiles map[string]*token.File
45 definedGlobally map[string]ast.Node
46 noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
47 anonDecls map[interface{}]string
48 cflags []string // CFlags from #cgo lines
49 ldflags []string // LDFlags from #cgo lines
50 visitedFiles map[string][]byte
51 cgoHeaders []string
52 }
53 54 // cgoFile holds information only for a single Go file (with one or more
55 // `import "C"` statements).
56 type cgoFile struct {
57 *cgoPackage
58 file *ast.File
59 index int
60 defined map[string]ast.Node
61 names map[string]clangCursor
62 }
63 64 // elaboratedTypeInfo contains some information about an elaborated type
65 // (struct, union) found in the C AST.
66 type elaboratedTypeInfo struct {
67 typeExpr *ast.StructType
68 pos token.Pos
69 bitfields []bitfieldInfo
70 unionSize int64 // union size in bytes, nonzero when union getters/setters should be created
71 unionAlign int64 // union alignment in bytes
72 }
73 74 // bitfieldInfo contains information about a single bitfield in a struct. It
75 // keeps information about the start, end, and the special (renamed) base field
76 // of this bitfield.
77 type bitfieldInfo struct {
78 field *ast.Field
79 name string
80 pos token.Pos
81 startBit int64
82 endBit int64 // may be 0 meaning "until the end of the field"
83 }
84 85 // Information about a #cgo noescape line in the source code.
86 type noescapingFunc struct {
87 name string
88 pos token.Pos
89 used bool // true if used somewhere in the source (for proper error reporting)
90 }
91 92 // cgoAliases list type aliases between Go and C, for types that are equivalent
93 // in both languages. See addTypeAliases.
94 var cgoAliases = map[string]string{
95 "_Cgo_int8_t": "int8",
96 "_Cgo_int16_t": "int16",
97 "_Cgo_int32_t": "int32",
98 "_Cgo_int64_t": "int64",
99 "_Cgo_uint8_t": "uint8",
100 "_Cgo_uint16_t": "uint16",
101 "_Cgo_uint32_t": "uint32",
102 "_Cgo_uint64_t": "uint64",
103 "_Cgo_uintptr_t": "uintptr",
104 "_Cgo_float": "float32",
105 "_Cgo_double": "float64",
106 "_Cgo__Bool": "bool",
107 }
108 109 // builtinAliases are handled specially because they only exist on the Go side
110 // of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
111 var builtinAliases = []string{
112 "char",
113 "schar",
114 "uchar",
115 "short",
116 "ushort",
117 "int",
118 "uint",
119 "long",
120 "ulong",
121 "longlong",
122 "ulonglong",
123 }
124 125 // builtinAliasTypedefs lists some C types with ambiguous sizes that must be
126 // retrieved somehow from C. This is done by adding some typedefs to get the
127 // size of each type.
128 const builtinAliasTypedefs = `
129 # 1 "<cgo>"
130 typedef char _Cgo_char;
131 typedef signed char _Cgo_schar;
132 typedef unsigned char _Cgo_uchar;
133 typedef short _Cgo_short;
134 typedef unsigned short _Cgo_ushort;
135 typedef int _Cgo_int;
136 typedef unsigned int _Cgo_uint;
137 typedef long _Cgo_long;
138 typedef unsigned long _Cgo_ulong;
139 typedef long long _Cgo_longlong;
140 typedef unsigned long long _Cgo_ulonglong;
141 `
142 143 // First part of the generated Go file. Written here as Go because that's much
144 // easier than constructing the entire AST in memory.
145 // The string/bytes functions below implement C.CString etc. To make sure the
146 // runtime doesn't need to know the C int type, lengths are converted to uintptr
147 // first.
148 const generatedGoFilePrefixBase = `
149 import "syscall"
150 import "unsafe"
151 152 var _ unsafe.Pointer
153 154 //go:linkname _Cgo_CString runtime.cgo_CString
155 func _Cgo_CString(string) *_Cgo_char
156 157 //go:linkname _Cgo_GoString runtime.cgo_GoString
158 func _Cgo_GoString(*_Cgo_char) string
159 160 //go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
161 func _Cgo___GoStringN(*_Cgo_char, uintptr) string
162 163 func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
164 return _Cgo___GoStringN(cstr, uintptr(length))
165 }
166 167 //go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
168 func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
169 170 func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
171 return _Cgo___GoBytes(ptr, uintptr(length))
172 }
173 174 //go:linkname _Cgo___CBytes runtime.cgo_CBytes
175 func _Cgo___CBytes([]byte) unsafe.Pointer
176 177 func _Cgo_CBytes(b []byte) unsafe.Pointer {
178 return _Cgo___CBytes(b)
179 }
180 181 //go:linkname _Cgo___get_errno_num runtime.cgo_errno
182 func _Cgo___get_errno_num() uintptr
183 `
184 185 const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
186 func _Cgo___get_errno() error {
187 return syscall.Errno(_Cgo___get_errno_num())
188 }
189 `
190 191 // Windows uses fake errno values in the syscall package.
192 // See for example: https://github.com/golang/go/issues/23468
193 // Moxie uses mingw-w64 though, which does have defined errno values. Since the
194 // syscall package is the standard library one we can't change it, but we can
195 // map the errno values to match the values in the syscall package.
196 // Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
197 const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
198 var _Cgo___errno_mapping = [...]syscall.Errno{
199 1: syscall.EPERM,
200 2: syscall.ENOENT,
201 3: syscall.ESRCH,
202 4: syscall.EINTR,
203 5: syscall.EIO,
204 6: syscall.ENXIO,
205 7: syscall.E2BIG,
206 8: syscall.ENOEXEC,
207 9: syscall.EBADF,
208 10: syscall.ECHILD,
209 11: syscall.EAGAIN,
210 12: syscall.ENOMEM,
211 13: syscall.EACCES,
212 14: syscall.EFAULT,
213 16: syscall.EBUSY,
214 17: syscall.EEXIST,
215 18: syscall.EXDEV,
216 19: syscall.ENODEV,
217 20: syscall.ENOTDIR,
218 21: syscall.EISDIR,
219 22: syscall.EINVAL,
220 23: syscall.ENFILE,
221 24: syscall.EMFILE,
222 25: syscall.ENOTTY,
223 27: syscall.EFBIG,
224 28: syscall.ENOSPC,
225 29: syscall.ESPIPE,
226 30: syscall.EROFS,
227 31: syscall.EMLINK,
228 32: syscall.EPIPE,
229 33: syscall.EDOM,
230 34: syscall.ERANGE,
231 36: syscall.EDEADLK,
232 38: syscall.ENAMETOOLONG,
233 39: syscall.ENOLCK,
234 40: syscall.ENOSYS,
235 41: syscall.ENOTEMPTY,
236 42: syscall.EILSEQ,
237 }
238 239 func _Cgo___get_errno() error {
240 num := _Cgo___get_errno_num()
241 if num < uintptr(len(_Cgo___errno_mapping)) {
242 if mapped := _Cgo___errno_mapping[num]; mapped != 0 {
243 return mapped
244 }
245 }
246 return syscall.Errno(num)
247 }
248 `
249 250 // Process extracts `import "C"` statements from the AST, parses the comment
251 // with libclang, and modifies the AST to use this information. It returns a
252 // newly created *ast.File that should be added to the list of to-be-parsed
253 // files, the CGo header snippets that should be compiled (for inline
254 // functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
255 // hashes of the accessed C header files. If there is one or more error, it
256 // returns these in the []error slice but still modifies the AST.
257 func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, goos string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
258 p := &cgoPackage{
259 packageName: files[0].Name.Name,
260 currentDir: dir,
261 importPath: importPath,
262 fset: fset,
263 tokenFiles: map[string]*token.File{},
264 definedGlobally: map[string]ast.Node{},
265 noescapingFuncs: map[string]*noescapingFunc{},
266 anonDecls: map[interface{}]string{},
267 visitedFiles: map[string][]byte{},
268 }
269 270 // Add a new location for the following file.
271 generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
272 generatedTokenPos.SetLines([]int{0})
273 p.generatedPos = generatedTokenPos.Pos(0)
274 275 // Find the absolute path for this package.
276 packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
277 if err != nil {
278 return nil, nil, nil, nil, nil, []error{
279 scanner.Error{
280 Pos: fset.Position(files[0].Pos()),
281 Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
282 },
283 }
284 }
285 p.packageDir = filepath.Dir(packagePath)
286 287 // Construct a new in-memory AST for CGo declarations of this package.
288 // The first part is written as Go code that is then parsed, but more code
289 // is added later to the AST to declare functions, globals, etc.
290 goCode := "package " + files[0].Name.Name + "\n\n"
291 if goos == "windows" {
292 goCode += generatedGoFilePrefixWindows
293 } else {
294 goCode += generatedGoFilePrefixOther
295 }
296 p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
297 if err != nil {
298 // This is always a bug in the cgo package.
299 panic("unexpected error: " + err.Error())
300 }
301 p.cgoFiles = append(p.cgoFiles, p.generated)
302 // If the Comments field is not set to nil, the go/format package will get
303 // confused about where comments should go.
304 p.generated.Comments = nil
305 306 // Find `import "C"` C fragments in the file.
307 p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
308 for i, f := range files {
309 var cgoHeader string
310 for i := 0; i < len(f.Decls); i++ {
311 decl := f.Decls[i]
312 genDecl, ok := decl.(*ast.GenDecl)
313 if !ok {
314 continue
315 }
316 if len(genDecl.Specs) != 1 {
317 continue
318 }
319 spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
320 if !ok {
321 continue
322 }
323 path, err := strconv.Unquote(spec.Path.Value)
324 if err != nil {
325 // This should not happen. An import path that is not properly
326 // quoted should not exist in a correct AST.
327 panic("could not parse import path: " + err.Error())
328 }
329 if path != "C" {
330 continue
331 }
332 333 // Remove this import declaration.
334 f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
335 i--
336 337 if genDecl.Doc == nil {
338 continue
339 }
340 341 // Iterate through all parts of the CGo header. Note that every //
342 // line is a new comment.
343 position := fset.Position(genDecl.Doc.Pos())
344 fragment := fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)
345 for _, comment := range genDecl.Doc.List {
346 // Find all #cgo lines, extract and use their contents, and
347 // replace the lines with spaces (to preserve locations).
348 c := p.parseCGoPreprocessorLines(comment.Text, comment.Slash)
349 350 // Change the comment (which is still in Go syntax, with // and
351 // /* */ ) to a regular string by replacing the start/end
352 // markers of comments with spaces.
353 // It is similar to the Text() method but differs in that it
354 // doesn't strip anything and tries to keep all offsets correct
355 // by adding spaces and newlines where necessary.
356 if c[1] == '/' { /* comment */
357 c = " " + c[2:]
358 } else { // comment
359 c = " " + c[2:len(c)-2]
360 }
361 fragment += c + "\n"
362 }
363 cgoHeader += fragment
364 }
365 366 p.cgoHeaders[i] = cgoHeader
367 }
368 369 // Define CFlags that will be used while parsing the package.
370 // Disable _FORTIFY_SOURCE as it causes problems on macOS.
371 // Note that it is only disabled for memcpy (etc) calls made from Go, which
372 // have better alternatives anyway.
373 cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
374 cflagsForCGo = append(cflagsForCGo, p.cflags...)
375 376 // Retrieve types such as C.int, C.longlong, etc from C.
377 p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
378 gen := &ast.GenDecl{
379 TokPos: token.NoPos,
380 Tok: token.TYPE,
381 }
382 for _, name := range builtinAliases {
383 typeSpec := p.getIntegerType("_Cgo_"+name, names["_Cgo_"+name])
384 gen.Specs = append(gen.Specs, typeSpec)
385 }
386 p.generated.Decls = append(p.generated.Decls, gen)
387 })
388 389 // Process CGo imports for each file.
390 for i, f := range files {
391 cf := p.newCGoFile(f, i)
392 // These types are aliased with the corresponding types in C. For
393 // example, float in C is always float32 in Go.
394 cf.names["float"] = clangCursor{}
395 cf.names["double"] = clangCursor{}
396 cf.names["_Bool"] = clangCursor{}
397 // Now read all the names (identifies) that C defines in the header
398 // snippet.
399 cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
400 for _, name := range builtinAliases {
401 // Names such as C.int should not be obtained from C.
402 // This works around an issue in picolibc that has `#define int`
403 // in a header file.
404 delete(names, name)
405 }
406 astutil.Apply(f, func(cursor *astutil.Cursor) bool {
407 return cf.walker(cursor, names)
408 }, nil)
409 })
410 }
411 412 // Show an error when a #cgo noescape line isn't used in practice.
413 // This matches upstream Go. I think the goal is to avoid issues with
414 // misspelled function names, which seems very useful.
415 var unusedNoescapeLines []*noescapingFunc
416 for _, value := range p.noescapingFuncs {
417 if !value.used {
418 unusedNoescapeLines = append(unusedNoescapeLines, value)
419 }
420 }
421 sort.SliceStable(unusedNoescapeLines, func(i, j int) bool {
422 return unusedNoescapeLines[i].pos < unusedNoescapeLines[j].pos
423 })
424 for _, value := range unusedNoescapeLines {
425 p.addError(value.pos, fmt.Sprintf("function %#v in #cgo noescape line is not used", value.name))
426 }
427 428 // Print the newly generated in-memory AST, for debugging.
429 //ast.Print(fset, p.generated)
430 431 return p.cgoFiles, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
432 }
433 434 func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
435 return &cgoFile{
436 cgoPackage: p,
437 file: file,
438 index: index,
439 defined: make(map[string]ast.Node),
440 names: make(map[string]clangCursor),
441 }
442 }
443 444 // makePathsAbsolute converts some common path compiler flags (-I, -L) from
445 // relative flags into absolute flags, if they are relative. This is necessary
446 // because the C compiler is usually not invoked from the package path.
447 func (p *cgoPackage) makePathsAbsolute(args []string) {
448 nextIsPath := false
449 for i, arg := range args {
450 if nextIsPath {
451 if !filepath.IsAbs(arg) {
452 args[i] = filepath.Join(p.packageDir, arg)
453 }
454 }
455 if arg == "-I" || arg == "-L" {
456 nextIsPath = true
457 continue
458 }
459 if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") {
460 path := arg[2:]
461 if !filepath.IsAbs(path) {
462 args[i] = arg[:2] + filepath.Join(p.packageDir, path)
463 }
464 }
465 }
466 }
467 468 // parseCGoPreprocessorLines reads #cgo pseudo-preprocessor lines in the source
469 // text (import "C" fragment), stores their information such as CFLAGS, and
470 // returns the same text but with those #cgo lines replaced by spaces (to keep
471 // position offsets the same).
472 func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) string {
473 for {
474 // Extract the #cgo line, and replace it with spaces.
475 // Replacing with spaces makes sure that error locations are
476 // still correct, while not interfering with parsing in any way.
477 lineStart := strings.Index(text, "#cgo ")
478 if lineStart < 0 {
479 break
480 }
481 lineLen := strings.IndexByte(text[lineStart:], '\n')
482 if lineLen < 0 {
483 lineLen = len(text) - lineStart
484 }
485 lineEnd := lineStart + lineLen
486 line := text[lineStart:lineEnd]
487 spaces := make([]byte, len(line))
488 for i := range spaces {
489 spaces[i] = ' '
490 }
491 text = text[:lineStart] + string(spaces) + text[lineEnd:]
492 493 allFields := strings.Fields(line[4:])
494 switch allFields[0] {
495 case "noescape":
496 // The code indicates that pointer parameters will not be captured
497 // by the called C function.
498 if len(allFields) < 2 {
499 p.addErrorAfter(pos, text[:lineStart], "missing function name in #cgo noescape line")
500 continue
501 }
502 if len(allFields) > 2 {
503 p.addErrorAfter(pos, text[:lineStart], "multiple function names in #cgo noescape line")
504 continue
505 }
506 name := allFields[1]
507 p.noescapingFuncs[name] = &noescapingFunc{
508 name: name,
509 pos: pos,
510 used: false,
511 }
512 continue
513 case "nocallback":
514 // We don't do anything special when calling a C function, so there
515 // appears to be no optimization that we can do here.
516 // Accept, but ignore the parameter for compatibility.
517 continue
518 }
519 520 // Get the text before the colon in the #cgo directive.
521 colon := strings.IndexByte(line, ':')
522 if colon < 0 {
523 p.addErrorAfter(pos, text[:lineStart], "missing colon in #cgo line")
524 continue
525 }
526 527 // Extract the fields before the colon. These fields are a list
528 // of build tags and the C environment variable.
529 fields := strings.Fields(line[4:colon])
530 if len(fields) == 0 {
531 p.addErrorAfter(pos, text[:lineStart+colon-1], "invalid #cgo line")
532 continue
533 }
534 535 if len(fields) > 1 {
536 p.addErrorAfter(pos, text[:lineStart+5], "not implemented: build constraints in #cgo line")
537 continue
538 }
539 540 name := fields[len(fields)-1]
541 value := line[colon+1:]
542 switch name {
543 case "CFLAGS":
544 flags, err := shlex.Split(value)
545 if err != nil {
546 // TODO: find the exact location where the error happened.
547 p.addErrorAfter(pos, text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
548 continue
549 }
550 if err := checkCompilerFlags(name, flags); err != nil {
551 p.addErrorAfter(pos, text[:lineStart+colon+1], err.Error())
552 continue
553 }
554 p.makePathsAbsolute(flags)
555 p.cflags = append(p.cflags, flags...)
556 case "LDFLAGS":
557 flags, err := shlex.Split(value)
558 if err != nil {
559 // TODO: find the exact location where the error happened.
560 p.addErrorAfter(pos, text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
561 continue
562 }
563 if err := checkLinkerFlags(name, flags); err != nil {
564 p.addErrorAfter(pos, text[:lineStart+colon+1], err.Error())
565 continue
566 }
567 p.makePathsAbsolute(flags)
568 p.ldflags = append(p.ldflags, flags...)
569 default:
570 startPos := strings.LastIndex(line[4:colon], name) + 4
571 p.addErrorAfter(pos, text[:lineStart+startPos], "invalid #cgo line: "+name)
572 continue
573 }
574 }
575 return text
576 }
577 578 // makeUnionField creates a new struct from an existing *elaboratedTypeInfo,
579 // that has just a single field that must be accessed through special accessors.
580 // It returns nil when there is an error. In case of an error, that error has
581 // already been added to the list of errors using p.addError.
582 func (p *cgoPackage) makeUnionField(typ *elaboratedTypeInfo) *ast.StructType {
583 unionFieldTypeName, ok := map[int64]string{
584 1: "uint8",
585 2: "uint16",
586 4: "uint32",
587 8: "uint64",
588 }[typ.unionAlign]
589 if !ok {
590 p.addError(typ.typeExpr.Struct, fmt.Sprintf("expected union alignment to be one of 1, 2, 4, or 8, but got %d", typ.unionAlign))
591 return nil
592 }
593 var unionFieldType ast.Expr = &ast.Ident{
594 NamePos: token.NoPos,
595 Name: unionFieldTypeName,
596 }
597 if typ.unionSize != typ.unionAlign {
598 // A plain struct{uintX} isn't enough, we have to make a
599 // struct{[N]uintX} to make the union big enough.
600 if typ.unionSize/typ.unionAlign*typ.unionAlign != typ.unionSize {
601 p.addError(typ.typeExpr.Struct, fmt.Sprintf("union alignment (%d) must be a multiple of union alignment (%d)", typ.unionSize, typ.unionAlign))
602 return nil
603 }
604 unionFieldType = &ast.ArrayType{
605 Len: &ast.BasicLit{
606 Kind: token.INT,
607 Value: strconv.FormatInt(typ.unionSize/typ.unionAlign, 10),
608 },
609 Elt: unionFieldType,
610 }
611 }
612 return &ast.StructType{
613 Struct: typ.typeExpr.Struct,
614 Fields: &ast.FieldList{
615 Opening: typ.typeExpr.Fields.Opening,
616 List: []*ast.Field{{
617 Names: []*ast.Ident{
618 {
619 NamePos: typ.typeExpr.Fields.Opening,
620 Name: "$union",
621 },
622 },
623 Type: unionFieldType,
624 }},
625 Closing: typ.typeExpr.Fields.Closing,
626 },
627 }
628 }
629 630 // createUnionAccessor creates a function that returns a typed pointer to a
631 // union field for each field in a union. For example:
632 //
633 // func (union *C.union_1) unionfield_d() *float64 {
634 // return (*float64)(unsafe.Pointer(&union.$union))
635 // }
636 //
637 // Where C.union_1 is defined as:
638 //
639 // type C.union_1 struct{
640 // $union uint64
641 // }
642 //
643 // The returned pointer can be used to get or set the field, or get the pointer
644 // to a subfield.
645 func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
646 if len(field.Names) != 1 {
647 panic("number of names in union field must be exactly 1")
648 }
649 fieldName := field.Names[0]
650 pos := fieldName.NamePos
651 652 // The method receiver.
653 receiver := &ast.SelectorExpr{
654 X: &ast.Ident{
655 NamePos: pos,
656 Name: "union",
657 Obj: nil,
658 },
659 Sel: &ast.Ident{
660 NamePos: pos,
661 Name: "$union",
662 },
663 }
664 665 // Get the address of the $union field.
666 receiverPtr := &ast.UnaryExpr{
667 Op: token.AND,
668 X: receiver,
669 }
670 671 // Cast to unsafe.Pointer.
672 sourcePointer := &ast.CallExpr{
673 Fun: &ast.SelectorExpr{
674 X: &ast.Ident{Name: "unsafe"},
675 Sel: &ast.Ident{Name: "Pointer"},
676 },
677 Args: []ast.Expr{receiverPtr},
678 }
679 680 // Cast to the target pointer type.
681 targetPointer := &ast.CallExpr{
682 Lparen: pos,
683 Fun: &ast.ParenExpr{
684 Lparen: pos,
685 X: &ast.StarExpr{
686 X: field.Type,
687 },
688 Rparen: pos,
689 },
690 Args: []ast.Expr{sourcePointer},
691 Rparen: pos,
692 }
693 694 // Create the accessor function.
695 accessor := &ast.FuncDecl{
696 Recv: &ast.FieldList{
697 Opening: pos,
698 List: []*ast.Field{
699 {
700 Names: []*ast.Ident{
701 {
702 NamePos: pos,
703 Name: "union",
704 },
705 },
706 Type: &ast.StarExpr{
707 Star: pos,
708 X: &ast.Ident{
709 NamePos: pos,
710 Name: typeName,
711 Obj: nil,
712 },
713 },
714 },
715 },
716 Closing: pos,
717 },
718 Name: &ast.Ident{
719 NamePos: pos,
720 Name: "unionfield_" + fieldName.Name,
721 },
722 Type: &ast.FuncType{
723 Func: pos,
724 Params: &ast.FieldList{
725 Opening: pos,
726 Closing: pos,
727 },
728 Results: &ast.FieldList{
729 List: []*ast.Field{
730 {
731 Type: &ast.StarExpr{
732 Star: pos,
733 X: field.Type,
734 },
735 },
736 },
737 },
738 },
739 Body: &ast.BlockStmt{
740 Lbrace: pos,
741 List: []ast.Stmt{
742 &ast.ReturnStmt{
743 Return: pos,
744 Results: []ast.Expr{
745 targetPointer,
746 },
747 },
748 },
749 Rbrace: pos,
750 },
751 }
752 p.generated.Decls = append(p.generated.Decls, accessor)
753 }
754 755 // createBitfieldGetter creates a bitfield getter function like the following:
756 //
757 // func (s *C.struct_foo) bitfield_b() byte {
758 // return (s.__bitfield_1 >> 5) & 0x1
759 // }
760 func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string) {
761 // The value to return from the getter.
762 // Not complete: this is just an expression to get the complete field.
763 var result ast.Expr = &ast.SelectorExpr{
764 X: &ast.Ident{
765 NamePos: bitfield.pos,
766 Name: "s",
767 Obj: nil,
768 },
769 Sel: &ast.Ident{
770 NamePos: bitfield.pos,
771 Name: bitfield.field.Names[0].Name,
772 },
773 }
774 if bitfield.startBit != 0 {
775 // Shift to the right by .startBit so that fields that come before are
776 // shifted off.
777 result = &ast.BinaryExpr{
778 X: result,
779 OpPos: bitfield.pos,
780 Op: token.SHR,
781 Y: &ast.BasicLit{
782 ValuePos: bitfield.pos,
783 Kind: token.INT,
784 Value: strconv.FormatInt(bitfield.startBit, 10),
785 },
786 }
787 }
788 if bitfield.endBit != 0 {
789 // Mask off the high bits so that fields that come after this field are
790 // masked off.
791 and := (uint64(1) << uint64(bitfield.endBit-bitfield.startBit)) - 1
792 result = &ast.BinaryExpr{
793 X: result,
794 OpPos: bitfield.pos,
795 Op: token.AND,
796 Y: &ast.BasicLit{
797 ValuePos: bitfield.pos,
798 Kind: token.INT,
799 Value: "0x" + strconv.FormatUint(and, 16),
800 },
801 }
802 }
803 804 // Create the getter function.
805 getter := &ast.FuncDecl{
806 Recv: &ast.FieldList{
807 Opening: bitfield.pos,
808 List: []*ast.Field{
809 {
810 Names: []*ast.Ident{
811 {
812 NamePos: bitfield.pos,
813 Name: "s",
814 Obj: &ast.Object{
815 Kind: ast.Var,
816 Name: "s",
817 Decl: nil,
818 },
819 },
820 },
821 Type: &ast.StarExpr{
822 Star: bitfield.pos,
823 X: &ast.Ident{
824 NamePos: bitfield.pos,
825 Name: typeName,
826 Obj: nil,
827 },
828 },
829 },
830 },
831 Closing: bitfield.pos,
832 },
833 Name: &ast.Ident{
834 NamePos: bitfield.pos,
835 Name: "bitfield_" + bitfield.name,
836 },
837 Type: &ast.FuncType{
838 Func: bitfield.pos,
839 Params: &ast.FieldList{
840 Opening: bitfield.pos,
841 Closing: bitfield.pos,
842 },
843 Results: &ast.FieldList{
844 List: []*ast.Field{
845 {
846 Type: bitfield.field.Type,
847 },
848 },
849 },
850 },
851 Body: &ast.BlockStmt{
852 Lbrace: bitfield.pos,
853 List: []ast.Stmt{
854 &ast.ReturnStmt{
855 Return: bitfield.pos,
856 Results: []ast.Expr{
857 result,
858 },
859 },
860 },
861 Rbrace: bitfield.pos,
862 },
863 }
864 p.generated.Decls = append(p.generated.Decls, getter)
865 }
866 867 // createBitfieldSetter creates a bitfield setter function like the following:
868 //
869 // func (s *C.struct_foo) set_bitfield_b(value byte) {
870 // s.__bitfield_1 = s.__bitfield_1 ^ 0x60 | ((value & 1) << 5)
871 // }
872 //
873 // Or the following:
874 //
875 // func (s *C.struct_foo) set_bitfield_c(value byte) {
876 // s.__bitfield_1 = s.__bitfield_1 & 0x3f | (value << 6)
877 // }
878 func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string) {
879 // The full field with all bitfields.
880 var field ast.Expr = &ast.SelectorExpr{
881 X: &ast.Ident{
882 NamePos: bitfield.pos,
883 Name: "s",
884 Obj: nil,
885 },
886 Sel: &ast.Ident{
887 NamePos: bitfield.pos,
888 Name: bitfield.field.Names[0].Name,
889 },
890 }
891 // The value to insert into the field.
892 var valueToInsert ast.Expr = &ast.Ident{
893 NamePos: bitfield.pos,
894 Name: "value",
895 }
896 897 if bitfield.endBit != 0 {
898 // Make sure the value is in range with a mask.
899 valueToInsert = &ast.BinaryExpr{
900 X: valueToInsert,
901 OpPos: bitfield.pos,
902 Op: token.AND,
903 Y: &ast.BasicLit{
904 ValuePos: bitfield.pos,
905 Kind: token.INT,
906 Value: "0x" + strconv.FormatUint((uint64(1)<<uint64(bitfield.endBit-bitfield.startBit))-1, 16),
907 },
908 }
909 // Create a mask for the AND NOT operation.
910 mask := ((uint64(1) << uint64(bitfield.endBit-bitfield.startBit)) - 1) << uint64(bitfield.startBit)
911 // Zero the bits in the field that will soon be inserted.
912 field = &ast.BinaryExpr{
913 X: field,
914 OpPos: bitfield.pos,
915 Op: token.AND_NOT,
916 Y: &ast.BasicLit{
917 ValuePos: bitfield.pos,
918 Kind: token.INT,
919 Value: "0x" + strconv.FormatUint(mask, 16),
920 },
921 }
922 } else { // bitfield.endBit == 0
923 // We don't know exactly how many high bits should be zeroed. So we do
924 // something different: keep the low bits with a mask and OR the new
925 // value with it.
926 mask := (uint64(1) << uint64(bitfield.startBit)) - 1
927 // Extract the lower bits.
928 field = &ast.BinaryExpr{
929 X: field,
930 OpPos: bitfield.pos,
931 Op: token.AND,
932 Y: &ast.BasicLit{
933 ValuePos: bitfield.pos,
934 Kind: token.INT,
935 Value: "0x" + strconv.FormatUint(mask, 16),
936 },
937 }
938 }
939 940 // Bitwise OR with the new value (after the new value has been shifted).
941 field = &ast.BinaryExpr{
942 X: field,
943 OpPos: bitfield.pos,
944 Op: token.OR,
945 Y: &ast.BinaryExpr{
946 X: valueToInsert,
947 OpPos: bitfield.pos,
948 Op: token.SHL,
949 Y: &ast.BasicLit{
950 ValuePos: bitfield.pos,
951 Kind: token.INT,
952 Value: strconv.FormatInt(bitfield.startBit, 10),
953 },
954 },
955 }
956 957 // Create the setter function.
958 setter := &ast.FuncDecl{
959 Recv: &ast.FieldList{
960 Opening: bitfield.pos,
961 List: []*ast.Field{
962 {
963 Names: []*ast.Ident{
964 {
965 NamePos: bitfield.pos,
966 Name: "s",
967 Obj: &ast.Object{
968 Kind: ast.Var,
969 Name: "s",
970 Decl: nil,
971 },
972 },
973 },
974 Type: &ast.StarExpr{
975 Star: bitfield.pos,
976 X: &ast.Ident{
977 NamePos: bitfield.pos,
978 Name: typeName,
979 Obj: nil,
980 },
981 },
982 },
983 },
984 Closing: bitfield.pos,
985 },
986 Name: &ast.Ident{
987 NamePos: bitfield.pos,
988 Name: "set_bitfield_" + bitfield.name,
989 },
990 Type: &ast.FuncType{
991 Func: bitfield.pos,
992 Params: &ast.FieldList{
993 Opening: bitfield.pos,
994 List: []*ast.Field{
995 {
996 Names: []*ast.Ident{
997 {
998 NamePos: bitfield.pos,
999 Name: "value",
1000 Obj: nil,
1001 },
1002 },
1003 Type: bitfield.field.Type,
1004 },
1005 },
1006 Closing: bitfield.pos,
1007 },
1008 },
1009 Body: &ast.BlockStmt{
1010 Lbrace: bitfield.pos,
1011 List: []ast.Stmt{
1012 &ast.AssignStmt{
1013 Lhs: []ast.Expr{
1014 &ast.SelectorExpr{
1015 X: &ast.Ident{
1016 NamePos: bitfield.pos,
1017 Name: "s",
1018 Obj: nil,
1019 },
1020 Sel: &ast.Ident{
1021 NamePos: bitfield.pos,
1022 Name: bitfield.field.Names[0].Name,
1023 },
1024 },
1025 },
1026 TokPos: bitfield.pos,
1027 Tok: token.ASSIGN,
1028 Rhs: []ast.Expr{
1029 field,
1030 },
1031 },
1032 },
1033 Rbrace: bitfield.pos,
1034 },
1035 }
1036 p.generated.Decls = append(p.generated.Decls, setter)
1037 }
1038 1039 // isEquivalentAST returns whether the given two AST nodes are equivalent as far
1040 // as CGo is concerned. This is used to check that C types, globals, etc defined
1041 // in different CGo header snippets are actually the same type (and probably
1042 // even defined in the same header file, just in different translation units).
1043 func (p *cgoPackage) isEquivalentAST(a, b ast.Node) bool {
1044 switch node := a.(type) {
1045 case *ast.ArrayType:
1046 b, ok := b.(*ast.ArrayType)
1047 if !ok {
1048 return false
1049 }
1050 if !p.isEquivalentAST(node.Len, b.Len) {
1051 return false
1052 }
1053 return p.isEquivalentAST(node.Elt, b.Elt)
1054 case *ast.BasicLit:
1055 b, ok := b.(*ast.BasicLit)
1056 if !ok {
1057 return false
1058 }
1059 // Note: this comparison is not correct in general ("1e2" equals "100"),
1060 // but is correct for its use in the cgo package.
1061 return node.Value == b.Value
1062 case *ast.CommentGroup:
1063 b, ok := b.(*ast.CommentGroup)
1064 if !ok {
1065 return false
1066 }
1067 if len(node.List) != len(b.List) {
1068 return false
1069 }
1070 for i, c := range node.List {
1071 if c.Text != b.List[i].Text {
1072 return false
1073 }
1074 }
1075 return true
1076 case *ast.FieldList:
1077 b, ok := b.(*ast.FieldList)
1078 if !ok {
1079 return false
1080 }
1081 if node == nil || b == nil {
1082 return node == b
1083 }
1084 if len(node.List) != len(b.List) {
1085 return false
1086 }
1087 for i, f := range node.List {
1088 if !p.isEquivalentAST(f, b.List[i]) {
1089 return false
1090 }
1091 }
1092 return true
1093 case *ast.Field:
1094 b, ok := b.(*ast.Field)
1095 if !ok {
1096 return false
1097 }
1098 if !p.isEquivalentAST(node.Type, b.Type) {
1099 return false
1100 }
1101 if len(node.Names) != len(b.Names) {
1102 return false
1103 }
1104 for i, name := range node.Names {
1105 if name.Name != b.Names[i].Name {
1106 return false
1107 }
1108 }
1109 return true
1110 case *ast.FuncDecl:
1111 b, ok := b.(*ast.FuncDecl)
1112 if !ok {
1113 return false
1114 }
1115 if node.Name.Name != b.Name.Name {
1116 return false
1117 }
1118 if node.Doc != b.Doc {
1119 if !p.isEquivalentAST(node.Doc, b.Doc) {
1120 return false
1121 }
1122 }
1123 if node.Recv != b.Recv {
1124 if !p.isEquivalentAST(node.Recv, b.Recv) {
1125 return false
1126 }
1127 }
1128 if !p.isEquivalentAST(node.Type.Params, b.Type.Params) {
1129 return false
1130 }
1131 return p.isEquivalentAST(node.Type.Results, b.Type.Results)
1132 case *ast.GenDecl:
1133 b, ok := b.(*ast.GenDecl)
1134 if !ok {
1135 return false
1136 }
1137 if node.Doc != b.Doc {
1138 if !p.isEquivalentAST(node.Doc, b.Doc) {
1139 return false
1140 }
1141 }
1142 if len(node.Specs) != len(b.Specs) {
1143 return false
1144 }
1145 for i, s := range node.Specs {
1146 if !p.isEquivalentAST(s, b.Specs[i]) {
1147 return false
1148 }
1149 }
1150 return true
1151 case *ast.Ident:
1152 b, ok := b.(*ast.Ident)
1153 if !ok {
1154 return false
1155 }
1156 return node.Name == b.Name
1157 case *ast.SelectorExpr:
1158 b, ok := b.(*ast.SelectorExpr)
1159 if !ok {
1160 return false
1161 }
1162 if !p.isEquivalentAST(node.Sel, b.Sel) {
1163 return false
1164 }
1165 return p.isEquivalentAST(node.X, b.X)
1166 case *ast.StarExpr:
1167 b, ok := b.(*ast.StarExpr)
1168 if !ok {
1169 return false
1170 }
1171 return p.isEquivalentAST(node.X, b.X)
1172 case *ast.StructType:
1173 b, ok := b.(*ast.StructType)
1174 if !ok {
1175 return false
1176 }
1177 return p.isEquivalentAST(node.Fields, b.Fields)
1178 case *ast.TypeSpec:
1179 b, ok := b.(*ast.TypeSpec)
1180 if !ok {
1181 return false
1182 }
1183 if node.Name.Name != b.Name.Name {
1184 return false
1185 }
1186 if node.Assign.IsValid() != b.Assign.IsValid() {
1187 return false
1188 }
1189 return p.isEquivalentAST(node.Type, b.Type)
1190 case *ast.ValueSpec:
1191 b, ok := b.(*ast.ValueSpec)
1192 if !ok {
1193 return false
1194 }
1195 if len(node.Names) != len(b.Names) {
1196 return false
1197 }
1198 for i, name := range node.Names {
1199 if name.Name != b.Names[i].Name {
1200 return false
1201 }
1202 }
1203 if node.Type != b.Type && !p.isEquivalentAST(node.Type, b.Type) {
1204 return false
1205 }
1206 if len(node.Values) != len(b.Values) {
1207 return false
1208 }
1209 for i, value := range node.Values {
1210 if !p.isEquivalentAST(value, b.Values[i]) {
1211 return false
1212 }
1213 }
1214 return true
1215 case nil:
1216 p.addError(token.NoPos, "internal error: AST node is nil")
1217 return true
1218 default:
1219 p.addError(a.Pos(), fmt.Sprintf("internal error: unknown AST node: %T", a))
1220 return true
1221 }
1222 }
1223 1224 // getPos returns node.Pos(), and tries to obtain a closely related position if
1225 // that fails.
1226 func getPos(node ast.Node) token.Pos {
1227 pos := node.Pos()
1228 if pos.IsValid() {
1229 return pos
1230 }
1231 if decl, ok := node.(*ast.GenDecl); ok {
1232 // *ast.GenDecl often doesn't have TokPos defined, so look at the first
1233 // spec.
1234 return getPos(decl.Specs[0])
1235 }
1236 return token.NoPos
1237 }
1238 1239 // getUnnamedDeclName creates a name (with the given prefix) for the given C
1240 // declaration. This is used for structs, unions, and enums that are often
1241 // defined without a name and used in a typedef.
1242 func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
1243 if name, ok := p.anonDecls[itf]; ok {
1244 return name
1245 }
1246 name := prefix + strconv.Itoa(len(p.anonDecls))
1247 p.anonDecls[itf] = name
1248 return name
1249 }
1250 1251 // getASTDeclName will declare the given C AST node (if not already defined) and
1252 // will return its name, in the form of C.foo.
1253 func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
1254 // Some types are defined in stdint.h and map directly to a particular Go
1255 // type.
1256 if alias := cgoAliases["_Cgo_"+name]; alias != "" {
1257 return alias
1258 }
1259 node := f.getASTDeclNode(name, found)
1260 if node, ok := node.(*ast.FuncDecl); ok {
1261 if !iscall {
1262 return node.Name.Name + "$funcaddr"
1263 }
1264 return node.Name.Name
1265 }
1266 return "_Cgo_" + name
1267 }
1268 1269 // getASTDeclNode will declare the given C AST node (if not already defined) and
1270 // returns it.
1271 func (f *cgoFile) getASTDeclNode(name string, found clangCursor) ast.Node {
1272 if node, ok := f.defined[name]; ok {
1273 // Declaration was found in the current file, so return it immediately.
1274 return node
1275 }
1276 1277 if node, ok := f.definedGlobally[name]; ok {
1278 // Declaration was previously created, but not for the current file. It
1279 // may be different (because it comes from a different CGo snippet), so
1280 // we need to check whether the AST for this definition is equivalent.
1281 f.defined[name] = nil
1282 newNode, _ := f.createASTNode(name, found)
1283 if !f.isEquivalentAST(node, newNode) {
1284 // It's not. Return a nice error with both locations.
1285 // Original cgo reports an error like
1286 // cgo: inconsistent definitions for C.myint
1287 // which is far less helpful.
1288 f.addError(getPos(node), name+" defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
1289 }
1290 f.defined[name] = node
1291 return node
1292 }
1293 1294 // The declaration has no AST node. Create it now.
1295 f.defined[name] = nil
1296 node, extra := f.createASTNode(name, found)
1297 f.defined[name] = node
1298 switch node := node.(type) {
1299 case *ast.FuncDecl:
1300 if strings.HasPrefix(node.Doc.List[0].Text, "//export _Cgo_static_") {
1301 // Static function. Only accessible in the current Go file.
1302 globalName := strings.TrimPrefix(node.Doc.List[0].Text, "//export ")
1303 // Make an alias. Normally this is done using the alias function
1304 // attribute, but MacOS for some reason doesn't support this (even
1305 // though the linker has support for aliases in the form of N_INDR).
1306 // Therefore, create an actual function for MacOS.
1307 var params []string
1308 for _, param := range node.Type.Params.List {
1309 params = append(params, param.Names[0].Name)
1310 }
1311 callInst := fmt.Sprintf("%s(%s);", name, strings.Join(params, ", "))
1312 if node.Type.Results != nil {
1313 callInst = "return " + callInst
1314 }
1315 aliasDeclaration := fmt.Sprintf(`
1316 #ifdef __APPLE__
1317 %s {
1318 %s
1319 }
1320 #else
1321 extern __typeof(%s) %s __attribute__((alias(%#v)));
1322 #endif
1323 `, extra.(string), callInst, name, globalName, name)
1324 f.cgoHeaders[f.index] += "\n\n" + aliasDeclaration
1325 } else {
1326 // Regular (non-static) function.
1327 f.definedGlobally[name] = node
1328 }
1329 f.generated.Decls = append(f.generated.Decls, node)
1330 // Also add a declaration like the following:
1331 // var C.foo$funcaddr unsafe.Pointer
1332 f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
1333 Tok: token.VAR,
1334 Specs: []ast.Spec{
1335 &ast.ValueSpec{
1336 Names: []*ast.Ident{{Name: node.Name.Name + "$funcaddr"}},
1337 Type: &ast.SelectorExpr{
1338 X: &ast.Ident{Name: "unsafe"},
1339 Sel: &ast.Ident{Name: "Pointer"},
1340 },
1341 },
1342 },
1343 })
1344 case *ast.GenDecl:
1345 f.definedGlobally[name] = node
1346 f.generated.Decls = append(f.generated.Decls, node)
1347 case *ast.TypeSpec:
1348 f.definedGlobally[name] = node
1349 f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
1350 Tok: token.TYPE,
1351 Specs: []ast.Spec{node},
1352 })
1353 case nil:
1354 // Node may be nil in case of an error. In that case, just don't add it
1355 // as a declaration.
1356 default:
1357 panic("unexpected AST node")
1358 }
1359 1360 // If this is a struct or union it may need bitfields or union accessor
1361 // methods.
1362 switch elaboratedType := extra.(type) {
1363 case *elaboratedTypeInfo:
1364 // Add struct bitfields.
1365 for _, bitfield := range elaboratedType.bitfields {
1366 f.createBitfieldGetter(bitfield, "_Cgo_"+name)
1367 f.createBitfieldSetter(bitfield, "_Cgo_"+name)
1368 }
1369 if elaboratedType.unionSize != 0 {
1370 // Create union getters/setters.
1371 for _, field := range elaboratedType.typeExpr.Fields.List {
1372 if len(field.Names) != 1 {
1373 f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
1374 continue
1375 }
1376 f.createUnionAccessor(field, "_Cgo_"+name)
1377 }
1378 }
1379 }
1380 1381 return node
1382 }
1383 1384 // walker replaces all "C".<something> expressions to literal "C.<something>"
1385 // expressions. Such expressions are impossible to write in Go (a dot cannot be
1386 // used in the middle of a name) so in practice all C identifiers live in a
1387 // separate namespace (no _Cgo_ hacks like in gc).
1388 func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
1389 switch node := cursor.Node().(type) {
1390 case *ast.AssignStmt:
1391 // An assign statement could be something like this:
1392 //
1393 // val, errno := C.some_func()
1394 //
1395 // Check whether it looks like that, and if so, read the errno value and
1396 // return it as the second return value. The call will be transformed
1397 // into something like this:
1398 //
1399 // val, errno := C.some_func(), C.__get_errno()
1400 if len(node.Lhs) != 2 || len(node.Rhs) != 1 {
1401 return true
1402 }
1403 rhs, ok := node.Rhs[0].(*ast.CallExpr)
1404 if !ok {
1405 return true
1406 }
1407 fun, ok := rhs.Fun.(*ast.SelectorExpr)
1408 if !ok {
1409 return true
1410 }
1411 x, ok := fun.X.(*ast.Ident)
1412 if !ok {
1413 return true
1414 }
1415 if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
1416 // Replace "C"."some_func" into "C.somefunc".
1417 rhs.Fun = &ast.Ident{
1418 NamePos: x.NamePos,
1419 Name: f.getASTDeclName(fun.Sel.Name, found, true),
1420 }
1421 // Add the errno value as the second value in the statement.
1422 node.Rhs = append(node.Rhs, &ast.CallExpr{
1423 Fun: &ast.Ident{
1424 NamePos: node.Lhs[1].End(),
1425 Name: "_Cgo___get_errno",
1426 },
1427 })
1428 }
1429 case *ast.CallExpr:
1430 fun, ok := node.Fun.(*ast.SelectorExpr)
1431 if !ok {
1432 return true
1433 }
1434 x, ok := fun.X.(*ast.Ident)
1435 if !ok {
1436 return true
1437 }
1438 if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
1439 node.Fun = &ast.Ident{
1440 NamePos: x.NamePos,
1441 Name: f.getASTDeclName(fun.Sel.Name, found, true),
1442 }
1443 }
1444 case *ast.SelectorExpr:
1445 x, ok := node.X.(*ast.Ident)
1446 if !ok {
1447 return true
1448 }
1449 if x.Name == "C" {
1450 name := "_Cgo_" + node.Sel.Name
1451 if found, ok := names[node.Sel.Name]; ok {
1452 name = f.getASTDeclName(node.Sel.Name, found, false)
1453 }
1454 cursor.Replace(&ast.Ident{
1455 NamePos: x.NamePos,
1456 Name: name,
1457 })
1458 }
1459 }
1460 return true
1461 }
1462 1463 // renameFieldKeywords renames all reserved words in Go to some other field name
1464 // with a "_" prefix. For example, it renames `type` to `_type`.
1465 //
1466 // See: https://golang.org/cmd/cgo/#hdr-Go_references_to_C
1467 func renameFieldKeywords(fieldList *ast.FieldList) {
1468 renameFieldName(fieldList, "type")
1469 }
1470 1471 // renameFieldName renames a given field name to a name with a "_" prepended. It
1472 // makes sure to do the same thing for any field sharing the same name.
1473 func renameFieldName(fieldList *ast.FieldList, name string) {
1474 var ident *ast.Ident
1475 for _, f := range fieldList.List {
1476 for _, n := range f.Names {
1477 if n.Name == name {
1478 ident = n
1479 }
1480 }
1481 }
1482 if ident == nil {
1483 return
1484 }
1485 renameFieldName(fieldList, "_"+name)
1486 ident.Name = "_" + ident.Name
1487 }
1488