9
9
"sort"
10
10
"strconv"
11
11
"strings"
12
+
13
+ "golang.org/x/tools/go/ast/astutil"
12
14
)
13
15
14
16
// fileInfo holds all Cgo-related information of a given *ast.File.
@@ -17,6 +19,7 @@ type fileInfo struct {
17
19
filename string
18
20
functions []* functionInfo
19
21
typedefs []* typedefInfo
22
+ globals []* globalInfo
20
23
importCPos token.Pos
21
24
}
22
25
@@ -41,6 +44,12 @@ type typedefInfo struct {
41
44
size int // size in bytes
42
45
}
43
46
47
+ // globalInfo contains information about a declared global variable in C.
48
+ type globalInfo struct {
49
+ name string
50
+ typeName string
51
+ }
52
+
44
53
// cgoAliases list type aliases between Go and C, for types that are equivalent
45
54
// in both languages. See addTypeAliases.
46
55
var cgoAliases = map [string ]string {
@@ -122,14 +131,17 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
122
131
// Declare functions found by libclang.
123
132
info .addFuncDecls ()
124
133
134
+ // Declare globals found by libclang.
135
+ info .addVarDecls ()
136
+
125
137
// Forward C types to Go types (like C.uint32_t -> uint32).
126
138
info .addTypeAliases ()
127
139
128
140
// Add type declarations for C types, declared using typeef in C.
129
141
info .addTypedefs ()
130
142
131
143
// 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 )
133
145
134
146
return nil
135
147
}
@@ -194,6 +206,43 @@ func (info *fileInfo) addFuncDecls() {
194
206
}
195
207
}
196
208
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
+
197
246
// addTypeAliases aliases some built-in Go types with their equivalent C types.
198
247
// It adds code like the following to the AST:
199
248
//
@@ -299,41 +348,22 @@ func (info *fileInfo) addTypedefs() {
299
348
info .Decls = append (info .Decls , gen )
300
349
}
301
350
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 )
314
359
if ! ok {
315
360
return true
316
361
}
317
362
if x .Name == "C" {
318
- node . Fun = & ast.Ident {
363
+ cursor . Replace ( & ast.Ident {
319
364
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
+ })
337
367
}
338
368
}
339
369
return true
0 commit comments