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