diff options
author | Ayke van Laethem <[email protected]> | 2021-09-17 02:41:45 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-09-28 18:44:11 +0200 |
commit | e02727679fc9cc24e59e6d5378eabd313605cfc6 (patch) | |
tree | 33fa8df895c98a5e58b9b9fe70eecf1d5ed55e75 /cgo/cgo.go | |
parent | 138add2b96532fee1f132a69b706bbc5d10ed457 (diff) | |
download | tinygo-e02727679fc9cc24e59e6d5378eabd313605cfc6.tar.gz tinygo-e02727679fc9cc24e59e6d5378eabd313605cfc6.zip |
builder, cgo: support function definitions in CGo headers
For example, the following did not work before but does work with this
change:
// int add(int a, int b) {
// return a + b;
// }
import "C"
func main() {
println("add:", C.add(3, 5))
}
Even better, the functions in the header are compiled together with the
rest of the Go code and so they can be optimized together! Currently,
inlining is not yet allowed but const-propagation across functions
works. This should be improved in the future.
Diffstat (limited to 'cgo/cgo.go')
-rw-r--r-- | cgo/cgo.go | 37 |
1 files changed, 31 insertions, 6 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go index a509e4b9a..8a47eb9c6 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -29,6 +29,7 @@ import ( type cgoPackage struct { generated *ast.File generatedPos token.Pos + cgoHeaders []string errors []error dir string fset *token.FileSet @@ -158,10 +159,11 @@ typedef unsigned long long _Cgo_ulonglong; // Process extracts `import "C"` statements from the AST, parses the comment // with libclang, and modifies the AST to use this information. It returns a // newly created *ast.File that should be added to the list of to-be-parsed -// files, 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) (*ast.File, []string, []string, map[string][]byte, []error) { +// files, the CGo header snippets that should be compiled (for inline +// 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) (*ast.File, []string, []string, []string, map[string][]byte, []error) { p := &cgoPackage{ dir: dir, fset: fset, @@ -184,7 +186,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string // Find the absolute path for this package. packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name()) if err != nil { - return nil, nil, nil, nil, []error{ + return nil, nil, nil, nil, nil, []error{ scanner.Error{ Pos: fset.Position(files[0].Pos()), Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error @@ -258,6 +260,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string // Find `import "C"` statements in the file. var statements []*ast.GenDecl for _, f := range files { + var cgoHeader string for i := 0; i < len(f.Decls); i++ { decl := f.Decls[i] genDecl, ok := decl.(*ast.GenDecl) @@ -284,11 +287,33 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string // Found a CGo statement. statements = append(statements, genDecl) + // Store the text of the CGo fragment so it can be compiled. This is + // for cases like these, where functions are defined directly in the + // header: + // // int add(int a, int b) { + // // return a + b; + // // } + // import "C" + if genDecl.Doc != nil { + position := fset.Position(genDecl.Doc.Pos()) + lines := []string{fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)} + for _, line := range strings.Split(getCommentText(genDecl.Doc), "\n") { + if strings.HasPrefix(strings.TrimSpace(line), "#cgo") { + line = "" + } + lines = append(lines, line) + } + fragment := strings.Join(lines, "\n") + cgoHeader += fragment + } + // Remove this import declaration. f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) i-- } + p.cgoHeaders = append(p.cgoHeaders, cgoHeader) + // Print the AST, for debugging. //ast.Print(fset, f) } @@ -436,7 +461,7 @@ 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, p.cflags, p.ldflags, p.visitedFiles, p.errors + return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors } // makePathsAbsolute converts some common path compiler flags (-I, -L) from |