github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/cgo/libclang.go (about) 1 package cgo 2 3 // This file parses a fragment of C with libclang and stores the result for AST 4 // modification. It does not touch the AST itself. 5 6 import ( 7 "crypto/sha256" 8 "crypto/sha512" 9 "encoding/hex" 10 "fmt" 11 "go/ast" 12 "go/scanner" 13 "go/token" 14 "path/filepath" 15 "strconv" 16 "strings" 17 "unsafe" 18 19 "tinygo.org/x/go-llvm" 20 ) 21 22 /* 23 #include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/ 24 #include <llvm/Config/llvm-config.h> 25 #include <stdlib.h> 26 #include <stdint.h> 27 28 // This struct should be ABI-compatible on all platforms (uintptr_t has the same 29 // alignment etc. as void*) but does not include void* pointers that are not 30 // always real pointers. 31 // The Go garbage collector assumes that all non-nil pointer-typed integers are 32 // actually pointers. This is not always true, as data[1] often contains 0x1, 33 // which is clearly not a valid pointer. Usually the GC won't catch this issue, 34 // but occasionally it will leading to a crash with a vague error message. 35 typedef struct { 36 enum CXCursorKind kind; 37 int xdata; 38 uintptr_t data[3]; 39 } GoCXCursor; 40 41 // Forwarding functions. They are implemented in libclang_stubs.c and forward to 42 // the real functions without doing anything else, thus they are entirely 43 // compatible with the versions without tinygo_ prefix. The only difference is 44 // the CXCursor type, which has been replaced with GoCXCursor. 45 GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu); 46 unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data); 47 CXString tinygo_clang_getCursorSpelling(GoCXCursor c); 48 CXString tinygo_clang_getCursorPrettyPrinted(GoCXCursor c, CXPrintingPolicy Policy); 49 CXPrintingPolicy tinygo_clang_getCursorPrintingPolicy(GoCXCursor c); 50 enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c); 51 CXType tinygo_clang_getCursorType(GoCXCursor c); 52 GoCXCursor tinygo_clang_getTypeDeclaration(CXType t); 53 CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c); 54 CXType tinygo_clang_getCursorResultType(GoCXCursor c); 55 int tinygo_clang_Cursor_getNumArguments(GoCXCursor c); 56 GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i); 57 enum CX_StorageClass tinygo_clang_Cursor_getStorageClass(GoCXCursor c); 58 CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c); 59 CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c); 60 CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c); 61 long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c); 62 CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c); 63 unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c); 64 unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c); 65 66 int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); 67 int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); 68 int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); 69 void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data); 70 */ 71 import "C" 72 73 // storedRefs stores references to types, used for clang_visitChildren. 74 var storedRefs refMap 75 76 var diagnosticSeverity = [...]string{ 77 C.CXDiagnostic_Ignored: "ignored", 78 C.CXDiagnostic_Note: "note", 79 C.CXDiagnostic_Warning: "warning", 80 C.CXDiagnostic_Error: "error", 81 C.CXDiagnostic_Fatal: "fatal", 82 } 83 84 // Alias so that cgo.go (which doesn't import Clang related stuff and is in 85 // theory decoupled from Clang) can also use this type. 86 type clangCursor = C.GoCXCursor 87 88 func init() { 89 // Check that we haven't messed up LLVM versioning. 90 // This can happen when llvm_config_*.go files in either this or the 91 // tinygo.org/x/go-llvm packages is incorrect. It should not ever happen 92 // with byollvm. 93 if C.LLVM_VERSION_STRING != llvm.Version { 94 panic("incorrect build: using LLVM version " + llvm.Version + " in the tinygo.org/x/llvm package, and version " + C.LLVM_VERSION_STRING + " in the ./cgo package") 95 } 96 } 97 98 func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) { 99 index := C.clang_createIndex(0, 0) 100 defer C.clang_disposeIndex(index) 101 102 // pretend to be a .c file 103 filenameC := C.CString(filename + "!cgo.c") 104 defer C.free(unsafe.Pointer(filenameC)) 105 106 fragmentC := C.CString(fragment) 107 defer C.free(unsafe.Pointer(fragmentC)) 108 109 unsavedFile := C.struct_CXUnsavedFile{ 110 Filename: filenameC, 111 Length: C.ulong(len(fragment)), 112 Contents: fragmentC, 113 } 114 115 // convert Go slice of strings to C array of strings. 116 cmdargsC := C.malloc(C.size_t(len(cflags)) * C.size_t(unsafe.Sizeof(uintptr(0)))) 117 defer C.free(cmdargsC) 118 cmdargs := (*[1 << 16]*C.char)(cmdargsC) 119 for i, cflag := range cflags { 120 s := C.CString(cflag) 121 cmdargs[i] = s 122 defer C.free(unsafe.Pointer(s)) 123 } 124 125 var unit C.CXTranslationUnit 126 errCode := C.clang_parseTranslationUnit2( 127 index, 128 filenameC, 129 (**C.char)(cmdargsC), C.int(len(cflags)), // command line args 130 &unsavedFile, 1, // unsaved files 131 C.CXTranslationUnit_DetailedPreprocessingRecord, 132 &unit) 133 if errCode != 0 { 134 // This is probably a bug in the usage of libclang. 135 panic("cgo: failed to parse source with libclang") 136 } 137 defer C.clang_disposeTranslationUnit(unit) 138 139 // Report parser and type errors. 140 if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 { 141 addDiagnostic := func(diagnostic C.CXDiagnostic) { 142 spelling := getString(C.clang_getDiagnosticSpelling(diagnostic)) 143 severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)] 144 location := C.clang_getDiagnosticLocation(diagnostic) 145 pos := f.getClangLocationPosition(location, unit) 146 f.addError(pos, severity+": "+spelling) 147 } 148 for i := 0; i < numDiagnostics; i++ { 149 diagnostic := C.clang_getDiagnostic(unit, C.uint(i)) 150 addDiagnostic(diagnostic) 151 152 // Child diagnostics (like notes on redefinitions). 153 diagnostics := C.clang_getChildDiagnostics(diagnostic) 154 for j := 0; j < int(C.clang_getNumDiagnosticsInSet(diagnostics)); j++ { 155 addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j))) 156 } 157 } 158 } 159 160 // Extract information required by CGo. 161 ref := storedRefs.Put(f) 162 defer storedRefs.Remove(ref) 163 cursor := C.tinygo_clang_getTranslationUnitCursor(unit) 164 C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref)) 165 166 // Determine files read during CGo processing, for caching. 167 inclusionCallback := func(includedFile C.CXFile) { 168 // Get full file path. 169 path := getString(C.clang_getFileName(includedFile)) 170 171 // Get contents of file (that should be in-memory). 172 size := C.size_t(0) 173 rawData := C.clang_getFileContents(unit, includedFile, &size) 174 if rawData == nil { 175 // Sanity check. This should (hopefully) never trigger. 176 panic("libclang: file contents was not loaded") 177 } 178 data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size] 179 180 // Hash the contents if it isn't hashed yet. 181 if _, ok := f.visitedFiles[path]; !ok { 182 // already stored 183 sum := sha512.Sum512_224(data) 184 f.visitedFiles[path] = sum[:] 185 } 186 } 187 inclusionCallbackRef := storedRefs.Put(inclusionCallback) 188 defer storedRefs.Remove(inclusionCallbackRef) 189 C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef)) 190 191 // Do all the C AST operations inside a callback. This makes sure that 192 // libclang related memory is only freed after it is not necessary anymore. 193 callback(f.names) 194 } 195 196 // Convert the AST node under the given Clang cursor to a Go AST node and return 197 // it. 198 func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) { 199 kind := C.tinygo_clang_getCursorKind(c) 200 pos := f.getCursorPosition(c) 201 switch kind { 202 case C.CXCursor_FunctionDecl: 203 cursorType := C.tinygo_clang_getCursorType(c) 204 numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) 205 obj := &ast.Object{ 206 Kind: ast.Fun, 207 Name: "C." + name, 208 } 209 exportName := name 210 localName := name 211 var stringSignature string 212 if C.tinygo_clang_Cursor_getStorageClass(c) == C.CX_SC_Static { 213 // A static function is assigned a globally unique symbol name based 214 // on the file path (like _Cgo_static_2d09198adbf58f4f4655_foo) and 215 // has a different Go name in the form of C.foo!symbols.go instead 216 // of just C.foo. 217 path := f.importPath + "/" + filepath.Base(f.fset.File(f.file.Pos()).Name()) 218 staticIDBuf := sha256.Sum256([]byte(path)) 219 staticID := hex.EncodeToString(staticIDBuf[:10]) 220 exportName = "_Cgo_static_" + staticID + "_" + name 221 localName = name + "!" + filepath.Base(path) 222 223 // Create a signature. This is necessary for MacOS to forward the 224 // call, because MacOS doesn't support aliases like ELF and PE do. 225 // (There is N_INDR but __attribute__((alias("..."))) doesn't work). 226 policy := C.tinygo_clang_getCursorPrintingPolicy(c) 227 defer C.clang_PrintingPolicy_dispose(policy) 228 C.clang_PrintingPolicy_setProperty(policy, C.CXPrintingPolicy_TerseOutput, 1) 229 stringSignature = getString(C.tinygo_clang_getCursorPrettyPrinted(c, policy)) 230 stringSignature = strings.Replace(stringSignature, " "+name+"(", " "+exportName+"(", 1) 231 stringSignature = strings.TrimPrefix(stringSignature, "static ") 232 } 233 args := make([]*ast.Field, numArgs) 234 decl := &ast.FuncDecl{ 235 Doc: &ast.CommentGroup{ 236 List: []*ast.Comment{ 237 { 238 Slash: pos - 1, 239 Text: "//export " + exportName, 240 }, 241 }, 242 }, 243 Name: &ast.Ident{ 244 NamePos: pos, 245 Name: "C." + localName, 246 Obj: obj, 247 }, 248 Type: &ast.FuncType{ 249 Func: pos, 250 Params: &ast.FieldList{ 251 Opening: pos, 252 List: args, 253 Closing: pos, 254 }, 255 }, 256 } 257 if C.clang_isFunctionTypeVariadic(cursorType) != 0 { 258 decl.Doc.List = append(decl.Doc.List, &ast.Comment{ 259 Slash: pos - 1, 260 Text: "//go:variadic", 261 }) 262 } 263 for i := 0; i < numArgs; i++ { 264 arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i)) 265 argName := getString(C.tinygo_clang_getCursorSpelling(arg)) 266 argType := C.clang_getArgType(cursorType, C.uint(i)) 267 if argName == "" { 268 argName = "$" + strconv.Itoa(i) 269 } 270 args[i] = &ast.Field{ 271 Names: []*ast.Ident{ 272 { 273 NamePos: pos, 274 Name: argName, 275 Obj: &ast.Object{ 276 Kind: ast.Var, 277 Name: argName, 278 Decl: decl, 279 }, 280 }, 281 }, 282 Type: f.makeDecayingASTType(argType, pos), 283 } 284 } 285 resultType := C.tinygo_clang_getCursorResultType(c) 286 if resultType.kind != C.CXType_Void { 287 decl.Type.Results = &ast.FieldList{ 288 List: []*ast.Field{ 289 { 290 Type: f.makeASTType(resultType, pos), 291 }, 292 }, 293 } 294 } 295 obj.Decl = decl 296 return decl, stringSignature 297 case C.CXCursor_StructDecl, C.CXCursor_UnionDecl: 298 typ := f.makeASTRecordType(c, pos) 299 typeName := "C." + name 300 typeExpr := typ.typeExpr 301 if typ.unionSize != 0 { 302 // Convert to a single-field struct type. 303 typeExpr = f.makeUnionField(typ) 304 } 305 obj := &ast.Object{ 306 Kind: ast.Typ, 307 Name: typeName, 308 } 309 typeSpec := &ast.TypeSpec{ 310 Name: &ast.Ident{ 311 NamePos: typ.pos, 312 Name: typeName, 313 Obj: obj, 314 }, 315 Type: typeExpr, 316 } 317 obj.Decl = typeSpec 318 return typeSpec, typ 319 case C.CXCursor_TypedefDecl: 320 typeName := "C." + name 321 underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) 322 obj := &ast.Object{ 323 Kind: ast.Typ, 324 Name: typeName, 325 } 326 typeSpec := &ast.TypeSpec{ 327 Name: &ast.Ident{ 328 NamePos: pos, 329 Name: typeName, 330 Obj: obj, 331 }, 332 Type: f.makeASTType(underlyingType, pos), 333 } 334 if underlyingType.kind != C.CXType_Enum { 335 typeSpec.Assign = pos 336 } 337 obj.Decl = typeSpec 338 return typeSpec, nil 339 case C.CXCursor_VarDecl: 340 cursorType := C.tinygo_clang_getCursorType(c) 341 typeExpr := f.makeASTType(cursorType, pos) 342 gen := &ast.GenDecl{ 343 TokPos: pos, 344 Tok: token.VAR, 345 Lparen: token.NoPos, 346 Rparen: token.NoPos, 347 Doc: &ast.CommentGroup{ 348 List: []*ast.Comment{ 349 { 350 Slash: pos - 1, 351 Text: "//go:extern " + name, 352 }, 353 }, 354 }, 355 } 356 obj := &ast.Object{ 357 Kind: ast.Var, 358 Name: "C." + name, 359 } 360 valueSpec := &ast.ValueSpec{ 361 Names: []*ast.Ident{{ 362 NamePos: pos, 363 Name: "C." + name, 364 Obj: obj, 365 }}, 366 Type: typeExpr, 367 } 368 obj.Decl = valueSpec 369 gen.Specs = append(gen.Specs, valueSpec) 370 return gen, nil 371 case C.CXCursor_MacroDefinition: 372 sourceRange := C.tinygo_clang_getCursorExtent(c) 373 start := C.clang_getRangeStart(sourceRange) 374 end := C.clang_getRangeEnd(sourceRange) 375 var file, endFile C.CXFile 376 var startOffset, endOffset C.unsigned 377 C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset) 378 if file == nil { 379 f.addError(pos, "internal error: could not find file where macro is defined") 380 return nil, nil 381 } 382 C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset) 383 if file != endFile { 384 f.addError(pos, "internal error: expected start and end location of a macro to be in the same file") 385 return nil, nil 386 } 387 if startOffset > endOffset { 388 f.addError(pos, "internal error: start offset of macro is after end offset") 389 return nil, nil 390 } 391 392 // read file contents and extract the relevant byte range 393 tu := C.tinygo_clang_Cursor_getTranslationUnit(c) 394 var size C.size_t 395 sourcePtr := C.clang_getFileContents(tu, file, &size) 396 if endOffset >= C.uint(size) { 397 f.addError(pos, "internal error: end offset of macro lies after end of file") 398 return nil, nil 399 } 400 source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset]) 401 if !strings.HasPrefix(source, name) { 402 f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source)) 403 return nil, nil 404 } 405 value := source[len(name):] 406 // Try to convert this #define into a Go constant expression. 407 expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value) 408 if scannerError != nil { 409 f.errors = append(f.errors, *scannerError) 410 return nil, nil 411 } 412 413 gen := &ast.GenDecl{ 414 TokPos: token.NoPos, 415 Tok: token.CONST, 416 Lparen: token.NoPos, 417 Rparen: token.NoPos, 418 } 419 obj := &ast.Object{ 420 Kind: ast.Con, 421 Name: "C." + name, 422 } 423 valueSpec := &ast.ValueSpec{ 424 Names: []*ast.Ident{{ 425 NamePos: pos, 426 Name: "C." + name, 427 Obj: obj, 428 }}, 429 Values: []ast.Expr{expr}, 430 } 431 obj.Decl = valueSpec 432 gen.Specs = append(gen.Specs, valueSpec) 433 return gen, nil 434 case C.CXCursor_EnumDecl: 435 obj := &ast.Object{ 436 Kind: ast.Typ, 437 Name: "C." + name, 438 } 439 underlying := C.tinygo_clang_getEnumDeclIntegerType(c) 440 // TODO: gc's CGo implementation uses types such as `uint32` for enums 441 // instead of types such as C.int, which are used here. 442 typeSpec := &ast.TypeSpec{ 443 Name: &ast.Ident{ 444 NamePos: pos, 445 Name: "C." + name, 446 Obj: obj, 447 }, 448 Assign: pos, 449 Type: f.makeASTType(underlying, pos), 450 } 451 obj.Decl = typeSpec 452 return typeSpec, nil 453 case C.CXCursor_EnumConstantDecl: 454 value := C.tinygo_clang_getEnumConstantDeclValue(c) 455 expr := &ast.BasicLit{ 456 ValuePos: pos, 457 Kind: token.INT, 458 Value: strconv.FormatInt(int64(value), 10), 459 } 460 gen := &ast.GenDecl{ 461 TokPos: token.NoPos, 462 Tok: token.CONST, 463 Lparen: token.NoPos, 464 Rparen: token.NoPos, 465 } 466 obj := &ast.Object{ 467 Kind: ast.Con, 468 Name: "C." + name, 469 } 470 valueSpec := &ast.ValueSpec{ 471 Names: []*ast.Ident{{ 472 NamePos: pos, 473 Name: "C." + name, 474 Obj: obj, 475 }}, 476 Values: []ast.Expr{expr}, 477 } 478 obj.Decl = valueSpec 479 gen.Specs = append(gen.Specs, valueSpec) 480 return gen, nil 481 default: 482 f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind)) 483 return nil, nil 484 } 485 } 486 487 func getString(clangString C.CXString) (s string) { 488 rawString := C.clang_getCString(clangString) 489 s = C.GoString(rawString) 490 C.clang_disposeString(clangString) 491 return 492 } 493 494 //export tinygo_clang_globals_visitor 495 func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { 496 f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile) 497 switch C.tinygo_clang_getCursorKind(c) { 498 case C.CXCursor_FunctionDecl: 499 name := getString(C.tinygo_clang_getCursorSpelling(c)) 500 f.names[name] = c 501 case C.CXCursor_StructDecl: 502 name := getString(C.tinygo_clang_getCursorSpelling(c)) 503 if name != "" { 504 f.names["struct_"+name] = c 505 } 506 case C.CXCursor_UnionDecl: 507 name := getString(C.tinygo_clang_getCursorSpelling(c)) 508 if name != "" { 509 f.names["union_"+name] = c 510 } 511 case C.CXCursor_TypedefDecl: 512 typedefType := C.tinygo_clang_getCursorType(c) 513 name := getString(C.clang_getTypedefName(typedefType)) 514 f.names[name] = c 515 case C.CXCursor_VarDecl: 516 name := getString(C.tinygo_clang_getCursorSpelling(c)) 517 f.names[name] = c 518 case C.CXCursor_MacroDefinition: 519 name := getString(C.tinygo_clang_getCursorSpelling(c)) 520 f.names[name] = c 521 case C.CXCursor_EnumDecl: 522 name := getString(C.tinygo_clang_getCursorSpelling(c)) 523 if name != "" { 524 // Named enum, which can be referenced from Go using C.enum_foo. 525 f.names["enum_"+name] = c 526 } 527 // The enum fields are in global scope, so recurse to visit them. 528 return C.CXChildVisit_Recurse 529 case C.CXCursor_EnumConstantDecl: 530 // We arrive here because of the "Recurse" above. 531 name := getString(C.tinygo_clang_getCursorSpelling(c)) 532 f.names[name] = c 533 } 534 return C.CXChildVisit_Continue 535 } 536 537 // Get the precise location in the source code. Used for uniquely identifying 538 // source locations. 539 func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} { 540 clangLocation := C.tinygo_clang_getCursorLocation(cursor) 541 var file C.CXFile 542 var line C.unsigned 543 var column C.unsigned 544 C.clang_getFileLocation(clangLocation, &file, &line, &column, nil) 545 location := token.Position{ 546 Filename: getString(C.clang_getFileName(file)), 547 Line: int(line), 548 Column: int(column), 549 } 550 if location.Filename == "" || location.Line == 0 { 551 // Not sure when this would happen, but protect from it anyway. 552 f.addError(pos, "could not find file/line information") 553 } 554 return location 555 } 556 557 // getCursorPosition returns a usable token.Pos from a libclang cursor. 558 func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos { 559 return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor)) 560 } 561 562 // getClangLocationPosition returns a usable token.Pos based on a libclang 563 // location and translation unit. If the file for this cursor has not been seen 564 // before, it is read from libclang (which already has the file in memory) and 565 // added to the token.FileSet. 566 func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.CXTranslationUnit) token.Pos { 567 var file C.CXFile 568 var line C.unsigned 569 var column C.unsigned 570 var offset C.unsigned 571 C.clang_getExpansionLocation(location, &file, &line, &column, &offset) 572 if line == 0 || file == nil { 573 // Invalid token. 574 return token.NoPos 575 } 576 filename := getString(C.clang_getFileName(file)) 577 if _, ok := p.tokenFiles[filename]; !ok { 578 // File has not been seen before in this package, add line information 579 // now by reading the file from libclang. 580 var size C.size_t 581 sourcePtr := C.clang_getFileContents(tu, file, &size) 582 source := ((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[:size:size] 583 lines := []int{0} 584 for i := 0; i < len(source)-1; i++ { 585 if source[i] == '\n' { 586 lines = append(lines, i+1) 587 } 588 } 589 f := p.fset.AddFile(filename, -1, int(size)) 590 f.SetLines(lines) 591 p.tokenFiles[filename] = f 592 // Add dummy file AST, to satisfy the type checker. 593 astFile := &ast.File{ 594 Package: f.Pos(0), 595 Name: ast.NewIdent(p.packageName), 596 } 597 setASTFileFields(astFile, f.Pos(0), f.Pos(int(size))) 598 p.cgoFiles = append(p.cgoFiles, astFile) 599 } 600 positionFile := p.tokenFiles[filename] 601 602 // Check for alternative line/column information (set with a line directive). 603 var filename2String C.CXString 604 var line2 C.unsigned 605 var column2 C.unsigned 606 C.clang_getPresumedLocation(location, &filename2String, &line2, &column2) 607 filename2 := getString(filename2String) 608 if filename2 != filename || line2 != line || column2 != column { 609 // The location was changed with a preprocessor directive. 610 // TODO: this only works for locations that are added in order. Adding 611 // line/column info to a file that already has line/column info after 612 // the given offset is ignored. 613 positionFile.AddLineColumnInfo(int(offset), filename2, int(line2), int(column2)) 614 } 615 616 return positionFile.Pos(int(offset)) 617 } 618 619 // addError is a utility function to add an error to the list of errors. It will 620 // convert the token position to a line/column position first, and call 621 // addErrorAt. 622 func (p *cgoPackage) addError(pos token.Pos, msg string) { 623 p.addErrorAt(p.fset.PositionFor(pos, true), msg) 624 } 625 626 // addErrorAfter is like addError, but adds the text `after` to the source 627 // location. 628 func (p *cgoPackage) addErrorAfter(pos token.Pos, after, msg string) { 629 position := p.fset.PositionFor(pos, true) 630 lines := strings.Split(after, "\n") 631 if len(lines) != 1 { 632 // Adjust lines. 633 // For why we can't just do pos+token.Pos(len(after)), see: 634 // https://github.com/golang/go/issues/35803 635 position.Line += len(lines) - 1 636 position.Column = len(lines[len(lines)-1]) + 1 637 } else { 638 position.Column += len(after) 639 } 640 p.addErrorAt(position, msg) 641 } 642 643 // addErrorAt is a utility function to add an error to the list of errors. 644 func (p *cgoPackage) addErrorAt(position token.Position, msg string) { 645 if filepath.IsAbs(position.Filename) { 646 // Relative paths for readability, like other Go parser errors. 647 relpath, err := filepath.Rel(p.currentDir, position.Filename) 648 if err == nil { 649 position.Filename = relpath 650 } 651 } 652 p.errors = append(p.errors, scanner.Error{ 653 Pos: position, 654 Msg: msg, 655 }) 656 } 657 658 // makeDecayingASTType does the same as makeASTType but takes care of decaying 659 // types (arrays in function parameters, etc). It is otherwise identical to 660 // makeASTType. 661 func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { 662 // Strip typedefs, if any. 663 underlyingType := typ 664 if underlyingType.kind == C.CXType_Elaborated { 665 // Starting with LLVM 16, the elaborated type is used for more types. 666 // According to the Clang documentation, the elaborated type has no 667 // semantic meaning so can be stripped (it is used to better convey type 668 // name information). 669 // Source: 670 // https://clang.llvm.org/doxygen/classclang_1_1ElaboratedType.html#details 671 // > The type itself is always "sugar", used to express what was written 672 // > in the source code but containing no additional semantic information. 673 underlyingType = C.clang_Type_getNamedType(underlyingType) 674 } 675 if underlyingType.kind == C.CXType_Typedef { 676 c := C.tinygo_clang_getTypeDeclaration(underlyingType) 677 underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c) 678 // TODO: support a chain of typedefs. At the moment, it seems to get 679 // stuck in an endless loop when trying to get to the most underlying 680 // type. 681 } 682 // Check for decaying type. An example would be an array type in a 683 // parameter. This declaration: 684 // void foo(char buf[6]); 685 // is the same as this one: 686 // void foo(char *buf); 687 // But this one: 688 // void bar(char buf[6][4]); 689 // equals this: 690 // void bar(char *buf[4]); 691 // so not all array dimensions should be stripped, just the first one. 692 // TODO: there are more kinds of decaying types. 693 if underlyingType.kind == C.CXType_ConstantArray { 694 // Apply type decaying. 695 pointeeType := C.clang_getElementType(underlyingType) 696 return &ast.StarExpr{ 697 Star: pos, 698 X: f.makeASTType(pointeeType, pos), 699 } 700 } 701 return f.makeASTType(typ, pos) 702 } 703 704 // makeASTType return the ast.Expr for the given libclang type. In other words, 705 // it converts a libclang type to a type in the Go AST. 706 func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { 707 var typeName string 708 switch typ.kind { 709 case C.CXType_Char_S, C.CXType_Char_U: 710 typeName = "C.char" 711 case C.CXType_SChar: 712 typeName = "C.schar" 713 case C.CXType_UChar: 714 typeName = "C.uchar" 715 case C.CXType_Short: 716 typeName = "C.short" 717 case C.CXType_UShort: 718 typeName = "C.ushort" 719 case C.CXType_Int: 720 typeName = "C.int" 721 case C.CXType_UInt: 722 typeName = "C.uint" 723 case C.CXType_Long: 724 typeName = "C.long" 725 case C.CXType_ULong: 726 typeName = "C.ulong" 727 case C.CXType_LongLong: 728 typeName = "C.longlong" 729 case C.CXType_ULongLong: 730 typeName = "C.ulonglong" 731 case C.CXType_Bool: 732 typeName = "bool" 733 case C.CXType_Float, C.CXType_Double, C.CXType_LongDouble: 734 switch C.clang_Type_getSizeOf(typ) { 735 case 4: 736 typeName = "float32" 737 case 8: 738 typeName = "float64" 739 default: 740 // Don't do anything, rely on the fallback code to show a somewhat 741 // sensible error message like "undeclared name: C.long double". 742 } 743 case C.CXType_Complex: 744 switch C.clang_Type_getSizeOf(typ) { 745 case 8: 746 typeName = "complex64" 747 case 16: 748 typeName = "complex128" 749 } 750 case C.CXType_Pointer: 751 pointeeType := C.clang_getPointeeType(typ) 752 if pointeeType.kind == C.CXType_Void { 753 // void* type is translated to Go as unsafe.Pointer 754 return &ast.SelectorExpr{ 755 X: &ast.Ident{ 756 NamePos: pos, 757 Name: "unsafe", 758 }, 759 Sel: &ast.Ident{ 760 NamePos: pos, 761 Name: "Pointer", 762 }, 763 } 764 } 765 return &ast.StarExpr{ 766 Star: pos, 767 X: f.makeASTType(pointeeType, pos), 768 } 769 case C.CXType_ConstantArray: 770 return &ast.ArrayType{ 771 Lbrack: pos, 772 Len: &ast.BasicLit{ 773 ValuePos: pos, 774 Kind: token.INT, 775 Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10), 776 }, 777 Elt: f.makeASTType(C.clang_getElementType(typ), pos), 778 } 779 case C.CXType_FunctionProto: 780 // Be compatible with gc, which uses the *[0]byte type for function 781 // pointer types. 782 // Return type [0]byte because this is a function type, not a pointer to 783 // this function type. 784 return &ast.ArrayType{ 785 Lbrack: pos, 786 Len: &ast.BasicLit{ 787 ValuePos: pos, 788 Kind: token.INT, 789 Value: "0", 790 }, 791 Elt: &ast.Ident{ 792 NamePos: pos, 793 Name: "byte", 794 }, 795 } 796 case C.CXType_Typedef: 797 name := getString(C.clang_getTypedefName(typ)) 798 c := C.tinygo_clang_getTypeDeclaration(typ) 799 return &ast.Ident{ 800 NamePos: pos, 801 Name: f.getASTDeclName(name, c, false), 802 } 803 case C.CXType_Elaborated: 804 underlying := C.clang_Type_getNamedType(typ) 805 switch underlying.kind { 806 case C.CXType_Record: 807 return f.makeASTType(underlying, pos) 808 case C.CXType_Enum: 809 return f.makeASTType(underlying, pos) 810 case C.CXType_Typedef: 811 return f.makeASTType(underlying, pos) 812 default: 813 typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind)) 814 f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling)) 815 typeName = "<unknown>" 816 } 817 case C.CXType_Record: 818 cursor := C.tinygo_clang_getTypeDeclaration(typ) 819 name := getString(C.tinygo_clang_getCursorSpelling(cursor)) 820 var cgoRecordPrefix string 821 switch C.tinygo_clang_getCursorKind(cursor) { 822 case C.CXCursor_StructDecl: 823 cgoRecordPrefix = "struct_" 824 case C.CXCursor_UnionDecl: 825 cgoRecordPrefix = "union_" 826 default: 827 // makeASTRecordType will create an appropriate error. 828 cgoRecordPrefix = "record_" 829 } 830 if name == "" || C.tinygo_clang_Cursor_isAnonymous(cursor) != 0 { 831 // Anonymous record, probably inside a typedef. 832 location := f.getUniqueLocationID(pos, cursor) 833 name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location) 834 } else { 835 name = cgoRecordPrefix + name 836 } 837 return &ast.Ident{ 838 NamePos: pos, 839 Name: f.getASTDeclName(name, cursor, false), 840 } 841 case C.CXType_Enum: 842 cursor := C.tinygo_clang_getTypeDeclaration(typ) 843 name := getString(C.tinygo_clang_getCursorSpelling(cursor)) 844 if name == "" { 845 // Anonymous enum, probably inside a typedef. 846 location := f.getUniqueLocationID(pos, cursor) 847 name = f.getUnnamedDeclName("_Ctype_enum___", location) 848 } else { 849 name = "enum_" + name 850 } 851 return &ast.Ident{ 852 NamePos: pos, 853 Name: f.getASTDeclName(name, cursor, false), 854 } 855 } 856 if typeName == "" { 857 // Report this as an error. 858 typeSpelling := getString(C.clang_getTypeSpelling(typ)) 859 typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind)) 860 f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling)) 861 typeName = "C.<unknown>" 862 } 863 return &ast.Ident{ 864 NamePos: pos, 865 Name: typeName, 866 } 867 } 868 869 // getIntegerType returns an AST node that defines types such as C.int. 870 func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec { 871 pos := p.getCursorPosition(cursor) 872 873 // Find a Go type that matches the size and signedness of the given C type. 874 underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor) 875 var goName string 876 typeSize := C.clang_Type_getSizeOf(underlyingType) 877 switch name { 878 case "C.char": 879 if typeSize != 1 { 880 // This happens for some very special purpose architectures 881 // (DSPs etc.) that are not currently targeted. 882 // https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/ 883 p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize)) 884 } 885 switch underlyingType.kind { 886 case C.CXType_Char_S: 887 goName = "int8" 888 case C.CXType_Char_U: 889 goName = "uint8" 890 } 891 case "C.schar", "C.short", "C.int", "C.long", "C.longlong": 892 switch typeSize { 893 case 1: 894 goName = "int8" 895 case 2: 896 goName = "int16" 897 case 4: 898 goName = "int32" 899 case 8: 900 goName = "int64" 901 } 902 case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong": 903 switch typeSize { 904 case 1: 905 goName = "uint8" 906 case 2: 907 goName = "uint16" 908 case 4: 909 goName = "uint32" 910 case 8: 911 goName = "uint64" 912 } 913 } 914 915 if goName == "" { // should not happen 916 p.addError(pos, "internal error: did not find Go type for C type "+name) 917 goName = "int" 918 } 919 920 // Construct an *ast.TypeSpec for this type. 921 obj := &ast.Object{ 922 Kind: ast.Typ, 923 Name: name, 924 } 925 spec := &ast.TypeSpec{ 926 Name: &ast.Ident{ 927 NamePos: pos, 928 Name: name, 929 Obj: obj, 930 }, 931 Type: &ast.Ident{ 932 NamePos: pos, 933 Name: goName, 934 }, 935 } 936 obj.Decl = spec 937 return spec 938 } 939 940 // makeASTRecordType parses a C record (struct or union) and translates it into 941 // a Go struct type. 942 func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo { 943 fieldList := &ast.FieldList{ 944 Opening: pos, 945 Closing: pos, 946 } 947 var bitfieldList []bitfieldInfo 948 inBitfield := false 949 bitfieldNum := 0 950 ref := storedRefs.Put(struct { 951 fieldList *ast.FieldList 952 file *cgoFile 953 inBitfield *bool 954 bitfieldNum *int 955 bitfieldList *[]bitfieldInfo 956 }{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList}) 957 defer storedRefs.Remove(ref) 958 C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref)) 959 renameFieldKeywords(fieldList) 960 switch C.tinygo_clang_getCursorKind(cursor) { 961 case C.CXCursor_StructDecl: 962 return &elaboratedTypeInfo{ 963 typeExpr: &ast.StructType{ 964 Struct: pos, 965 Fields: fieldList, 966 }, 967 pos: pos, 968 bitfields: bitfieldList, 969 } 970 case C.CXCursor_UnionDecl: 971 typeInfo := &elaboratedTypeInfo{ 972 typeExpr: &ast.StructType{ 973 Struct: pos, 974 Fields: fieldList, 975 }, 976 pos: pos, 977 bitfields: bitfieldList, 978 } 979 if len(fieldList.List) <= 1 { 980 // Useless union, treat it as a regular struct. 981 return typeInfo 982 } 983 if bitfieldList != nil { 984 // This is valid C... but please don't do this. 985 f.addError(pos, "bitfield in a union is not supported") 986 } 987 typ := C.tinygo_clang_getCursorType(cursor) 988 alignInBytes := int64(C.clang_Type_getAlignOf(typ)) 989 sizeInBytes := int64(C.clang_Type_getSizeOf(typ)) 990 if sizeInBytes == 0 { 991 f.addError(pos, "zero-length union is not supported") 992 } 993 typeInfo.unionSize = sizeInBytes 994 typeInfo.unionAlign = alignInBytes 995 return typeInfo 996 default: 997 cursorKind := C.tinygo_clang_getCursorKind(cursor) 998 cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind)) 999 f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling)) 1000 return &elaboratedTypeInfo{ 1001 typeExpr: &ast.StructType{ 1002 Struct: pos, 1003 }, 1004 pos: pos, 1005 } 1006 } 1007 } 1008 1009 //export tinygo_clang_struct_visitor 1010 func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { 1011 passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct { 1012 fieldList *ast.FieldList 1013 file *cgoFile 1014 inBitfield *bool 1015 bitfieldNum *int 1016 bitfieldList *[]bitfieldInfo 1017 }) 1018 fieldList := passed.fieldList 1019 f := passed.file 1020 inBitfield := passed.inBitfield 1021 bitfieldNum := passed.bitfieldNum 1022 bitfieldList := passed.bitfieldList 1023 pos := f.getCursorPosition(c) 1024 switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind { 1025 case C.CXCursor_FieldDecl: 1026 // Expected. This is a regular field. 1027 case C.CXCursor_StructDecl, C.CXCursor_UnionDecl: 1028 // Ignore. The next field will be the struct/union itself. 1029 return C.CXChildVisit_Continue 1030 default: 1031 cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind)) 1032 f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling)) 1033 return C.CXChildVisit_Continue 1034 } 1035 name := getString(C.tinygo_clang_getCursorSpelling(c)) 1036 if name == "" { 1037 // Assume this is a bitfield of 0 bits. 1038 // Warning: this is not necessarily true! 1039 return C.CXChildVisit_Continue 1040 } 1041 typ := C.tinygo_clang_getCursorType(c) 1042 field := &ast.Field{ 1043 Type: f.makeASTType(typ, f.getCursorPosition(c)), 1044 } 1045 offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name))) 1046 alignOf := int64(C.clang_Type_getAlignOf(typ) * 8) 1047 bitfieldOffset := offsetof % alignOf 1048 if bitfieldOffset != 0 { 1049 if C.tinygo_clang_Cursor_isBitField(c) != 1 { 1050 f.addError(pos, "expected a bitfield") 1051 return C.CXChildVisit_Continue 1052 } 1053 if !*inBitfield { 1054 *bitfieldNum++ 1055 } 1056 bitfieldName := "__bitfield_" + strconv.Itoa(*bitfieldNum) 1057 prevField := fieldList.List[len(fieldList.List)-1] 1058 if !*inBitfield { 1059 // The previous element also was a bitfield, but wasn't noticed 1060 // then. Add it now. 1061 *inBitfield = true 1062 *bitfieldList = append(*bitfieldList, bitfieldInfo{ 1063 field: prevField, 1064 name: prevField.Names[0].Name, 1065 startBit: 0, 1066 pos: prevField.Names[0].NamePos, 1067 }) 1068 prevField.Names[0].Name = bitfieldName 1069 prevField.Names[0].Obj.Name = bitfieldName 1070 } 1071 prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1] 1072 prevBitfield.endBit = bitfieldOffset 1073 *bitfieldList = append(*bitfieldList, bitfieldInfo{ 1074 field: prevField, 1075 name: name, 1076 startBit: bitfieldOffset, 1077 pos: pos, 1078 }) 1079 return C.CXChildVisit_Continue 1080 } 1081 *inBitfield = false 1082 field.Names = []*ast.Ident{ 1083 { 1084 NamePos: pos, 1085 Name: name, 1086 Obj: &ast.Object{ 1087 Kind: ast.Var, 1088 Name: name, 1089 Decl: field, 1090 }, 1091 }, 1092 } 1093 fieldList.List = append(fieldList.List, field) 1094 return C.CXChildVisit_Continue 1095 } 1096 1097 //export tinygo_clang_inclusion_visitor 1098 func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) { 1099 callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile)) 1100 callback(includedFile) 1101 }