Skip to content

Commit 2dc025e

Browse files
committed
cmd/fix: extend typechecker to use cgo types
If a file uses cgo, incorporate the types generated by running cgo. Update golang#23091 Change-Id: I10958fa7fd6027c2c96a9fd8a9658de35439719f Reviewed-on: https://go-review.googlesource.com/87616 Reviewed-by: Robert Griesemer <gri@golang.org>
1 parent d162a29 commit 2dc025e

File tree

3 files changed

+81
-5
lines changed

3 files changed

+81
-5
lines changed

src/cmd/fix/cftype.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,19 @@ var cftypeFix = fix{
3030
// and similar for other *Ref types.
3131
// This fix finds nils initializing these types and replaces the nils with 0s.
3232
func cftypefix(f *ast.File) bool {
33-
return typefix(f, func(s string) bool {
34-
return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref")
33+
var tc TypeConfig
34+
return typefix(f, &tc, func(s string) bool {
35+
return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref") &&
36+
(s == "C.CFTypeRef" || tc.External[s[:len(s)-3]+"GetTypeID"] == "func() C.CFTypeID")
3537
})
3638
}
3739

3840
// typefix replaces nil with 0 for all nils whose type, when passed to badType, returns true.
39-
func typefix(f *ast.File, badType func(string) bool) bool {
41+
func typefix(f *ast.File, tc *TypeConfig, badType func(string) bool) bool {
4042
if !imports(f, "C") {
4143
return false
4244
}
43-
typeof, _ := typecheck(&TypeConfig{}, f)
45+
typeof, _ := typecheck(tc, f)
4446

4547
// step 1: Find all the nils with the offending types.
4648
// Compute their replacement.

src/cmd/fix/jnitype.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ var jniFix = fix{
2727
// and similar for subtypes of jobject.
2828
// This fix finds nils initializing these types and replaces the nils with 0s.
2929
func jnifix(f *ast.File) bool {
30-
return typefix(f, func(s string) bool {
30+
var tc TypeConfig
31+
return typefix(f, &tc, func(s string) bool {
3132
switch s {
3233
case "C.jobject":
3334
return true

src/cmd/fix/typecheck.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ package main
77
import (
88
"fmt"
99
"go/ast"
10+
"go/parser"
1011
"go/token"
12+
"io/ioutil"
1113
"os"
14+
"os/exec"
15+
"path/filepath"
1216
"reflect"
17+
"runtime"
1318
"strings"
1419
)
1520

@@ -74,6 +79,11 @@ type TypeConfig struct {
7479
Type map[string]*Type
7580
Var map[string]string
7681
Func map[string]string
82+
83+
// External maps from a name to its type.
84+
// It provides additional typings not present in the Go source itself.
85+
// For now, the only additional typings are those generated by cgo.
86+
External map[string]string
7787
}
7888

7989
// 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
140150
*cfg1 = *cfg // make copy so we can add locally
141151
copied := false
142152

153+
// If we import "C", add types of cgo objects.
154+
cfg.External = map[string]string{}
155+
if imports(f, "C") {
156+
// Run cgo on gofmtFile(f)
157+
// Parse, extract decls from _cgo_gotypes.go
158+
// Map _Ctype_* types to C.* types.
159+
err := func() error {
160+
txt, err := gofmtFile(f)
161+
if err != nil {
162+
return err
163+
}
164+
dir, err := ioutil.TempDir(os.TempDir(), "fix_cgo_typecheck")
165+
if err != nil {
166+
return err
167+
}
168+
defer os.Remove(dir)
169+
err = ioutil.WriteFile(filepath.Join(dir, "in.go"), txt, 0600)
170+
if err != nil {
171+
return err
172+
}
173+
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "cgo", "-objdir", dir, "-srcdir", dir, "in.go")
174+
err = cmd.Run()
175+
if err != nil {
176+
return err
177+
}
178+
out, err := ioutil.ReadFile(filepath.Join(dir, "_cgo_gotypes.go"))
179+
if err != nil {
180+
return err
181+
}
182+
cgo, err := parser.ParseFile(token.NewFileSet(), "cgo.go", out, 0)
183+
if err != nil {
184+
return err
185+
}
186+
for _, decl := range cgo.Decls {
187+
fn, ok := decl.(*ast.FuncDecl)
188+
if !ok {
189+
continue
190+
}
191+
if strings.HasPrefix(fn.Name.Name, "_Cfunc_") {
192+
var params, results []string
193+
for _, p := range fn.Type.Params.List {
194+
t := gofmt(p.Type)
195+
t = strings.Replace(t, "_Ctype_", "C.", -1)
196+
params = append(params, t)
197+
}
198+
for _, r := range fn.Type.Results.List {
199+
t := gofmt(r.Type)
200+
t = strings.Replace(t, "_Ctype_", "C.", -1)
201+
results = append(results, t)
202+
}
203+
cfg.External["C."+fn.Name.Name[7:]] = joinFunc(params, results)
204+
}
205+
}
206+
return nil
207+
}()
208+
if err != nil {
209+
fmt.Printf("warning: no cgo types: %s\n", err)
210+
}
211+
}
212+
143213
// gather function declarations
144214
for _, decl := range f.Decls {
145215
fn, ok := decl.(*ast.FuncDecl)
@@ -434,6 +504,9 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
434504
}
435505
// Otherwise, use type of function to determine arguments.
436506
t := typeof[n.Fun]
507+
if t == "" {
508+
t = cfg.External[gofmt(n.Fun)]
509+
}
437510
in, out := splitFunc(t)
438511
if in == nil && out == nil {
439512
return

0 commit comments

Comments
 (0)