// and similar for other *Ref types.
// This fix finds nils initializing these types and replaces the nils with 0s.
func cftypefix(f *ast.File) bool {
- return typefix(f, func(s string) bool {
- return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref")
+ var tc TypeConfig
+ return typefix(f, &tc, func(s string) bool {
+ return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref") &&
+ (s == "C.CFTypeRef" || tc.External[s[:len(s)-3]+"GetTypeID"] == "func() C.CFTypeID")
})
}
// typefix replaces nil with 0 for all nils whose type, when passed to badType, returns true.
-func typefix(f *ast.File, badType func(string) bool) bool {
+func typefix(f *ast.File, tc *TypeConfig, badType func(string) bool) bool {
if !imports(f, "C") {
return false
}
- typeof, _ := typecheck(&TypeConfig{}, f)
+ typeof, _ := typecheck(tc, f)
// step 1: Find all the nils with the offending types.
// Compute their replacement.
import (
"fmt"
"go/ast"
+ "go/parser"
"go/token"
+ "io/ioutil"
"os"
+ "os/exec"
+ "path/filepath"
"reflect"
+ "runtime"
"strings"
)
Type map[string]*Type
Var map[string]string
Func map[string]string
+
+ // External maps from a name to its type.
+ // It provides additional typings not present in the Go source itself.
+ // For now, the only additional typings are those generated by cgo.
+ External map[string]string
}
// typeof returns the type of the given name, which may be of
*cfg1 = *cfg // make copy so we can add locally
copied := false
+ // If we import "C", add types of cgo objects.
+ cfg.External = map[string]string{}
+ if imports(f, "C") {
+ // Run cgo on gofmtFile(f)
+ // Parse, extract decls from _cgo_gotypes.go
+ // Map _Ctype_* types to C.* types.
+ err := func() error {
+ txt, err := gofmtFile(f)
+ if err != nil {
+ return err
+ }
+ dir, err := ioutil.TempDir(os.TempDir(), "fix_cgo_typecheck")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(dir)
+ err = ioutil.WriteFile(filepath.Join(dir, "in.go"), txt, 0600)
+ if err != nil {
+ return err
+ }
+ cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "cgo", "-objdir", dir, "-srcdir", dir, "in.go")
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ out, err := ioutil.ReadFile(filepath.Join(dir, "_cgo_gotypes.go"))
+ if err != nil {
+ return err
+ }
+ cgo, err := parser.ParseFile(token.NewFileSet(), "cgo.go", out, 0)
+ if err != nil {
+ return err
+ }
+ for _, decl := range cgo.Decls {
+ fn, ok := decl.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ if strings.HasPrefix(fn.Name.Name, "_Cfunc_") {
+ var params, results []string
+ for _, p := range fn.Type.Params.List {
+ t := gofmt(p.Type)
+ t = strings.Replace(t, "_Ctype_", "C.", -1)
+ params = append(params, t)
+ }
+ for _, r := range fn.Type.Results.List {
+ t := gofmt(r.Type)
+ t = strings.Replace(t, "_Ctype_", "C.", -1)
+ results = append(results, t)
+ }
+ cfg.External["C."+fn.Name.Name[7:]] = joinFunc(params, results)
+ }
+ }
+ return nil
+ }()
+ if err != nil {
+ fmt.Printf("warning: no cgo types: %s\n", err)
+ }
+ }
+
// gather function declarations
for _, decl := range f.Decls {
fn, ok := decl.(*ast.FuncDecl)
}
// Otherwise, use type of function to determine arguments.
t := typeof[n.Fun]
+ if t == "" {
+ t = cfg.External[gofmt(n.Fun)]
+ }
in, out := splitFunc(t)
if in == nil && out == nil {
return