99 "sort"
1010 "strconv"
1111 "strings"
12+
13+ "golang.org/x/tools/go/ast/astutil"
1214)
1315
1416// fileInfo holds all Cgo-related information of a given *ast.File.
@@ -17,6 +19,7 @@ type fileInfo struct {
1719 filename string
1820 functions []* functionInfo
1921 typedefs []* typedefInfo
22+ globals []* globalInfo
2023 importCPos token.Pos
2124}
2225
@@ -41,6 +44,12 @@ type typedefInfo struct {
4144 size int // size in bytes
4245}
4346
47+ // globalInfo contains information about a declared global variable in C.
48+ type globalInfo struct {
49+ name string
50+ typeName string
51+ }
52+
4453// cgoAliases list type aliases between Go and C, for types that are equivalent
4554// in both languages. See addTypeAliases.
4655var cgoAliases = map [string ]string {
@@ -122,14 +131,17 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
122131 // Declare functions found by libclang.
123132 info .addFuncDecls ()
124133
134+ // Declare globals found by libclang.
135+ info .addVarDecls ()
136+
125137 // Forward C types to Go types (like C.uint32_t -> uint32).
126138 info .addTypeAliases ()
127139
128140 // Add type declarations for C types, declared using typeef in C.
129141 info .addTypedefs ()
130142
131143 // Patch the AST to use the declared types and functions.
132- ast . Inspect (f , info .walker )
144+ f = astutil . Apply (f , info .walker , nil ).( * ast. File )
133145
134146 return nil
135147}
@@ -194,6 +206,43 @@ func (info *fileInfo) addFuncDecls() {
194206 }
195207}
196208
209+ // addVarDecls declares external C globals in the Go source.
210+ // It adds code like the following to the AST:
211+ //
212+ // var (
213+ // C.globalInt int
214+ // C.globalBool bool
215+ // // ...
216+ // )
217+ func (info * fileInfo ) addVarDecls () {
218+ gen := & ast.GenDecl {
219+ TokPos : info .importCPos ,
220+ Tok : token .VAR ,
221+ Lparen : info .importCPos ,
222+ Rparen : info .importCPos ,
223+ }
224+ for _ , global := range info .globals {
225+ obj := & ast.Object {
226+ Kind : ast .Typ ,
227+ Name : mapCgoType (global .name ),
228+ }
229+ valueSpec := & ast.ValueSpec {
230+ Names : []* ast.Ident {& ast.Ident {
231+ NamePos : info .importCPos ,
232+ Name : mapCgoType (global .name ),
233+ Obj : obj ,
234+ }},
235+ Type : & ast.Ident {
236+ NamePos : info .importCPos ,
237+ Name : mapCgoType (global .typeName ),
238+ },
239+ }
240+ obj .Decl = valueSpec
241+ gen .Specs = append (gen .Specs , valueSpec )
242+ }
243+ info .Decls = append (info .Decls , gen )
244+ }
245+
197246// addTypeAliases aliases some built-in Go types with their equivalent C types.
198247// It adds code like the following to the AST:
199248//
@@ -299,41 +348,22 @@ func (info *fileInfo) addTypedefs() {
299348 info .Decls = append (info .Decls , gen )
300349}
301350
302- // walker replaces all "C".<something> call expressions to literal
303- // "C.<something>" expressions. This is impossible to write in Go (a dot cannot
304- // be used in the middle of a name) so is used as a new namespace for C call
305- // expressions.
306- func (info * fileInfo ) walker (node ast.Node ) bool {
307- switch node := node .(type ) {
308- case * ast.CallExpr :
309- fun , ok := node .Fun .(* ast.SelectorExpr )
310- if ! ok {
311- return true
312- }
313- x , ok := fun .X .(* ast.Ident )
351+ // walker replaces all "C".<something> expressions to literal "C.<something>"
352+ // expressions. Such expressions are impossible to write in Go (a dot cannot be
353+ // used in the middle of a name) so in practice all C identifiers live in a
354+ // separate namespace (no _Cgo_ hacks like in gc).
355+ func (info * fileInfo ) walker (cursor * astutil.Cursor ) bool {
356+ switch node := cursor .Node ().(type ) {
357+ case * ast.SelectorExpr :
358+ x , ok := node .X .(* ast.Ident )
314359 if ! ok {
315360 return true
316361 }
317362 if x .Name == "C" {
318- node . Fun = & ast.Ident {
363+ cursor . Replace ( & ast.Ident {
319364 NamePos : x .NamePos ,
320- Name : mapCgoType (fun .Sel .Name ),
321- }
322- }
323- case * ast.ValueSpec :
324- typ , ok := node .Type .(* ast.SelectorExpr )
325- if ! ok {
326- return true
327- }
328- x , ok := typ .X .(* ast.Ident )
329- if ! ok {
330- return true
331- }
332- if x .Name == "C" {
333- node .Type = & ast.Ident {
334- NamePos : x .NamePos ,
335- Name : mapCgoType (typ .Sel .Name ),
336- }
365+ Name : mapCgoType (node .Sel .Name ),
366+ })
337367 }
338368 }
339369 return true
0 commit comments