]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/fix: extend typechecker to use cgo types
authorKeith Randall <khr@golang.org>
Fri, 12 Jan 2018 18:11:01 +0000 (10:11 -0800)
committerKeith Randall <khr@golang.org>
Wed, 17 Jan 2018 06:44:25 +0000 (06:44 +0000)
If a file uses cgo, incorporate the types generated by running cgo.

Update #23091

Change-Id: I10958fa7fd6027c2c96a9fd8a9658de35439719f
Reviewed-on: https://go-review.googlesource.com/87616
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/fix/cftype.go
src/cmd/fix/jnitype.go
src/cmd/fix/typecheck.go

index 841bd4dccb6285901c19728d98d808d903c5142e..5e742a4fdf779df9738d58ab96171fa2a956fa79 100644 (file)
@@ -30,17 +30,19 @@ var cftypeFix = fix{
 // 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.
index 29abe0f007830221ec10af316532f8ccfd35ff75..75ae570c4da418db072483895dddc642ff4f4afa 100644 (file)
@@ -27,7 +27,8 @@ var jniFix = fix{
 // and similar for subtypes of jobject.
 // This fix finds nils initializing these types and replaces the nils with 0s.
 func jnifix(f *ast.File) bool {
-       return typefix(f, func(s string) bool {
+       var tc TypeConfig
+       return typefix(f, &tc, func(s string) bool {
                switch s {
                case "C.jobject":
                        return true
index 58d915869d65d7f5d457f8e94337fc250c1f5406..a52a54202d1212ddef9bc3ea2c12a2d6a58848c3 100644 (file)
@@ -7,9 +7,14 @@ package main
 import (
        "fmt"
        "go/ast"
+       "go/parser"
        "go/token"
+       "io/ioutil"
        "os"
+       "os/exec"
+       "path/filepath"
        "reflect"
+       "runtime"
        "strings"
 )
 
@@ -74,6 +79,11 @@ type TypeConfig struct {
        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
@@ -140,6 +150,66 @@ func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, ass
        *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)
@@ -434,6 +504,9 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
                        }
                        // 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