Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,25 @@ func (g *Global) LinkName() string {
if g.linkName != "" {
return g.linkName
}
if name := g.CName(); name != "" {
return name
}
return g.RelString(nil)
}

func (g *Global) IsExtern() bool {
return g.extern
return g.extern || g.CName() != ""
}

// Return the name of the C global if this is a CGo wrapper. Otherwise, return a
// zero-length string.
func (g *Global) CName() string {
name := g.Name()
if strings.HasPrefix(name, "C.") {
// created by ../loader/cgo.go
return name[2:]
}
return ""
}

func (g *Global) Initializer() Value {
Expand Down
92 changes: 61 additions & 31 deletions loader/cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"sort"
"strconv"
"strings"

"golang.org/x/tools/go/ast/astutil"
)

// fileInfo holds all Cgo-related information of a given *ast.File.
Expand All @@ -17,6 +19,7 @@ type fileInfo struct {
filename string
functions []*functionInfo
typedefs []*typedefInfo
globals []*globalInfo
importCPos token.Pos
}

Expand All @@ -41,6 +44,12 @@ type typedefInfo struct {
size int // size in bytes
}

// globalInfo contains information about a declared global variable in C.
type globalInfo struct {
name string
typeName string
}

// cgoAliases list type aliases between Go and C, for types that are equivalent
// in both languages. See addTypeAliases.
var cgoAliases = map[string]string{
Expand Down Expand Up @@ -122,14 +131,17 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
// Declare functions found by libclang.
info.addFuncDecls()

// Declare globals found by libclang.
info.addVarDecls()

// Forward C types to Go types (like C.uint32_t -> uint32).
info.addTypeAliases()

// Add type declarations for C types, declared using typeef in C.
info.addTypedefs()

// Patch the AST to use the declared types and functions.
ast.Inspect(f, info.walker)
f = astutil.Apply(f, info.walker, nil).(*ast.File)

return nil
}
Expand Down Expand Up @@ -194,6 +206,43 @@ func (info *fileInfo) addFuncDecls() {
}
}

// addVarDecls declares external C globals in the Go source.
// It adds code like the following to the AST:
//
// var (
// C.globalInt int
// C.globalBool bool
// // ...
// )
func (info *fileInfo) addVarDecls() {
gen := &ast.GenDecl{
TokPos: info.importCPos,
Tok: token.VAR,
Lparen: info.importCPos,
Rparen: info.importCPos,
}
for _, global := range info.globals {
obj := &ast.Object{
Kind: ast.Typ,
Name: mapCgoType(global.name),
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{&ast.Ident{
NamePos: info.importCPos,
Name: mapCgoType(global.name),
Obj: obj,
}},
Type: &ast.Ident{
NamePos: info.importCPos,
Name: mapCgoType(global.typeName),
},
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
}
info.Decls = append(info.Decls, gen)
}

// addTypeAliases aliases some built-in Go types with their equivalent C types.
// It adds code like the following to the AST:
//
Expand Down Expand Up @@ -299,41 +348,22 @@ func (info *fileInfo) addTypedefs() {
info.Decls = append(info.Decls, gen)
}

// walker replaces all "C".<something> call expressions to literal
// "C.<something>" expressions. This is impossible to write in Go (a dot cannot
// be used in the middle of a name) so is used as a new namespace for C call
// expressions.
func (info *fileInfo) walker(node ast.Node) bool {
switch node := node.(type) {
case *ast.CallExpr:
fun, ok := node.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
x, ok := fun.X.(*ast.Ident)
// walker replaces all "C".<something> expressions to literal "C.<something>"
// expressions. Such expressions are impossible to write in Go (a dot cannot be
// used in the middle of a name) so in practice all C identifiers live in a
// separate namespace (no _Cgo_ hacks like in gc).
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
switch node := cursor.Node().(type) {
case *ast.SelectorExpr:
x, ok := node.X.(*ast.Ident)
if !ok {
return true
}
if x.Name == "C" {
node.Fun = &ast.Ident{
cursor.Replace(&ast.Ident{
NamePos: x.NamePos,
Name: mapCgoType(fun.Sel.Name),
}
}
case *ast.ValueSpec:
typ, ok := node.Type.(*ast.SelectorExpr)
if !ok {
return true
}
x, ok := typ.X.(*ast.Ident)
if !ok {
return true
}
if x.Name == "C" {
node.Type = &ast.Ident{
NamePos: x.NamePos,
Name: mapCgoType(typ.Sel.Name),
}
Name: mapCgoType(node.Sel.Name),
})
}
}
return true
Expand Down
8 changes: 8 additions & 0 deletions loader/libclang.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.in
oldName: underlyingTypeName,
size: int(typeSize),
})
case C.CXCursor_VarDecl:
name := getString(C.clang_getCursorSpelling(c))
cursorType := C.clang_getCursorType(c)
cursorTypeName := getString(C.clang_getTypeSpelling(cursorType))
info.globals = append(info.globals, &globalInfo{
name: name,
typeName: cursorTypeName,
})
}
return C.CXChildVisit_Continue
}
Expand Down
2 changes: 2 additions & 0 deletions testdata/cgo/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "main.h"

int global = 3;

int fortytwo() {
return 42;
}
Expand Down
1 change: 1 addition & 0 deletions testdata/cgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ func main() {
println("myint size:", int(unsafe.Sizeof(x)))
var y C.longlong = -(1 << 40)
println("longlong:", y)
println("global:", C.global)
}
1 change: 1 addition & 0 deletions testdata/cgo/main.h
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
typedef short myint;
int add(int a, int b);
extern int global;
1 change: 1 addition & 0 deletions testdata/cgo/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ add: 8
myint: 3 5
myint size: 2
longlong: -1099511627776
global: 3