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  }