aboutsummaryrefslogtreecommitdiffhomepage
path: root/cgo/cgo.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2022-04-13 21:34:43 +0200
committerRon Evans <[email protected]>2022-09-16 14:05:17 +0200
commit5551ec7a1ed9d59764cfdf5a73b23dc40365a6f8 (patch)
tree0fcff842574327f6615ed0909ed57efaf726dba5 /cgo/cgo.go
parent91104b2f276348e251a25e9e58e7faafe781358f (diff)
downloadtinygo-5551ec7a1ed9d59764cfdf5a73b23dc40365a6f8.tar.gz
tinygo-5551ec7a1ed9d59764cfdf5a73b23dc40365a6f8.zip
cgo: implement support for static functions
Diffstat (limited to 'cgo/cgo.go')
-rw-r--r--cgo/cgo.go73
1 files changed, 57 insertions, 16 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go
index e1be8ab49..dfdd223ed 100644
--- a/cgo/cgo.go
+++ b/cgo/cgo.go
@@ -32,6 +32,7 @@ type cgoPackage struct {
errors []error
currentDir string // current working directory
packageDir string // full path to the package to process
+ importPath string
fset *token.FileSet
tokenFiles map[string]*token.File
definedGlobally map[string]ast.Node
@@ -39,12 +40,15 @@ type cgoPackage struct {
cflags []string // CFlags from #cgo lines
ldflags []string // LDFlags from #cgo lines
visitedFiles map[string][]byte
+ cgoHeaders []string
}
// cgoFile holds information only for a single Go file (with one or more
// `import "C"` statements).
type cgoFile struct {
*cgoPackage
+ file *ast.File
+ index int
defined map[string]ast.Node
names map[string]clangCursor
}
@@ -158,9 +162,10 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
// hashes of the accessed C header files. If there is one or more error, it
// returns these in the []error slice but still modifies the AST.
-func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
+func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
p := &cgoPackage{
currentDir: dir,
+ importPath: importPath,
fset: fset,
tokenFiles: map[string]*token.File{},
definedGlobally: map[string]ast.Node{},
@@ -210,13 +215,13 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
}
}
// Patch some types, for example *C.char in C.CString.
- cf := p.newCGoFile()
+ cf := p.newCGoFile(nil, -1) // dummy *cgoFile for the walker
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
return cf.walker(cursor, nil)
}, nil)
// Find `import "C"` C fragments in the file.
- cgoHeaders := make([]string, len(files)) // combined CGo header fragment for each file
+ p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
for i, f := range files {
var cgoHeader string
for i := 0; i < len(f.Decls); i++ {
@@ -275,7 +280,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
cgoHeader += fragment
}
- cgoHeaders[i] = cgoHeader
+ p.cgoHeaders[i] = cgoHeader
}
// Define CFlags that will be used while parsing the package.
@@ -289,7 +294,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
}
// Retrieve types such as C.int, C.longlong, etc from C.
- p.newCGoFile().readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
+ p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
gen := &ast.GenDecl{
TokPos: token.NoPos,
Tok: token.TYPE,
@@ -303,8 +308,8 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Process CGo imports for each file.
for i, f := range files {
- cf := p.newCGoFile()
- cf.readNames(cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
+ cf := p.newCGoFile(f, i)
+ cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
for _, name := range builtinAliases {
// Names such as C.int should not be obtained from C.
// This works around an issue in picolibc that has `#define int`
@@ -320,12 +325,14 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated)
- return p.generated, cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
+ return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
}
-func (p *cgoPackage) newCGoFile() *cgoFile {
+func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
return &cgoFile{
cgoPackage: p,
+ file: file,
+ index: index,
defined: make(map[string]ast.Node),
names: make(map[string]clangCursor),
}
@@ -1117,8 +1124,11 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st
return alias
}
node := f.getASTDeclNode(name, found, iscall)
- if _, ok := node.(*ast.FuncDecl); ok && !iscall {
- return "C." + name + "$funcaddr"
+ if node, ok := node.(*ast.FuncDecl); ok {
+ if !iscall {
+ return node.Name.Name + "$funcaddr"
+ }
+ return node.Name.Name
}
return "C." + name
}
@@ -1142,7 +1152,7 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
// Original cgo reports an error like
// cgo: inconsistent definitions for C.myint
// which is far less helpful.
- f.addError(getPos(node), "defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
+ f.addError(getPos(node), name+" defined previously at "+f.fset.Position(getPos(newNode)).String()+" with a different type")
}
f.defined[name] = node
return node
@@ -1150,11 +1160,39 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
// The declaration has no AST node. Create it now.
f.defined[name] = nil
- node, elaboratedType := f.createASTNode(name, found)
+ node, extra := f.createASTNode(name, found)
f.defined[name] = node
- f.definedGlobally[name] = node
switch node := node.(type) {
case *ast.FuncDecl:
+ if strings.HasPrefix(node.Doc.List[0].Text, "//export _Cgo_static_") {
+ // Static function. Only accessible in the current Go file.
+ globalName := strings.TrimPrefix(node.Doc.List[0].Text, "//export ")
+ // Make an alias. Normally this is done using the alias function
+ // attribute, but MacOS for some reason doesn't support this (even
+ // though the linker has support for aliases in the form of N_INDR).
+ // Therefore, create an actual function for MacOS.
+ var params []string
+ for _, param := range node.Type.Params.List {
+ params = append(params, param.Names[0].Name)
+ }
+ callInst := fmt.Sprintf("%s(%s);", name, strings.Join(params, ", "))
+ if node.Type.Results != nil {
+ callInst = "return " + callInst
+ }
+ aliasDeclaration := fmt.Sprintf(`
+#ifdef __APPLE__
+%s {
+ %s
+}
+#else
+extern __typeof(%s) %s __attribute__((alias(%#v)));
+#endif
+`, extra.(string), callInst, name, globalName, name)
+ f.cgoHeaders[f.index] += "\n\n" + aliasDeclaration
+ } else {
+ // Regular (non-static) function.
+ f.definedGlobally[name] = node
+ }
f.generated.Decls = append(f.generated.Decls, node)
// Also add a declaration like the following:
// var C.foo$funcaddr unsafe.Pointer
@@ -1162,7 +1200,7 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
Tok: token.VAR,
Specs: []ast.Spec{
&ast.ValueSpec{
- Names: []*ast.Ident{{Name: "C." + name + "$funcaddr"}},
+ Names: []*ast.Ident{{Name: node.Name.Name + "$funcaddr"}},
Type: &ast.SelectorExpr{
X: &ast.Ident{Name: "unsafe"},
Sel: &ast.Ident{Name: "Pointer"},
@@ -1171,8 +1209,10 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
},
})
case *ast.GenDecl:
+ f.definedGlobally[name] = node
f.generated.Decls = append(f.generated.Decls, node)
case *ast.TypeSpec:
+ f.definedGlobally[name] = node
f.generated.Decls = append(f.generated.Decls, &ast.GenDecl{
Tok: token.TYPE,
Specs: []ast.Spec{node},
@@ -1186,7 +1226,8 @@ func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) as
// If this is a struct or union it may need bitfields or union accessor
// methods.
- if elaboratedType != nil {
+ switch elaboratedType := extra.(type) {
+ case *elaboratedTypeInfo:
// Add struct bitfields.
for _, bitfield := range elaboratedType.bitfields {
f.createBitfieldGetter(bitfield, "C."+name)