diff options
author | Ayke van Laethem <[email protected]> | 2022-04-13 21:34:43 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2022-09-16 14:05:17 +0200 |
commit | 5551ec7a1ed9d59764cfdf5a73b23dc40365a6f8 (patch) | |
tree | 0fcff842574327f6615ed0909ed57efaf726dba5 /cgo/cgo.go | |
parent | 91104b2f276348e251a25e9e58e7faafe781358f (diff) | |
download | tinygo-5551ec7a1ed9d59764cfdf5a73b23dc40365a6f8.tar.gz tinygo-5551ec7a1ed9d59764cfdf5a73b23dc40365a6f8.zip |
cgo: implement support for static functions
Diffstat (limited to 'cgo/cgo.go')
-rw-r--r-- | cgo/cgo.go | 73 |
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) |