| // Copyright 2017 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import ( | |
| "go/ast" | |
| "go/token" | |
| "reflect" | |
| "strings" | |
| ) | |
| func init() { | |
| register(cftypeFix) | |
| } | |
| var cftypeFix = fix{ | |
| name: "cftype", | |
| date: "2017-09-27", | |
| f: cftypefix, | |
| desc: `Fixes initializers and casts of C.*Ref and JNI types`, | |
| disabled: false, | |
| } | |
| // Old state: | |
| // type CFTypeRef unsafe.Pointer | |
| // New state: | |
| // type CFTypeRef uintptr | |
| // 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") && s != "C.CFAllocatorRef" | |
| }) | |
| } | |
| // 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 { | |
| if !imports(f, "C") { | |
| return false | |
| } | |
| typeof, _ := typecheck(&TypeConfig{}, f) | |
| changed := false | |
| // step 1: Find all the nils with the offending types. | |
| // Compute their replacement. | |
| badNils := map[interface{}]ast.Expr{} | |
| walk(f, func(n interface{}) { | |
| if i, ok := n.(*ast.Ident); ok && i.Name == "nil" && badType(typeof[n]) { | |
| badNils[n] = &ast.BasicLit{ValuePos: i.NamePos, Kind: token.INT, Value: "0"} | |
| } | |
| }) | |
| // step 2: find all uses of the bad nils, replace them with 0. | |
| // There's no easy way to map from an ast.Expr to all the places that use them, so | |
| // we use reflect to find all such references. | |
| if len(badNils) > 0 { | |
| exprType := reflect.TypeOf((*ast.Expr)(nil)).Elem() | |
| exprSliceType := reflect.TypeOf(([]ast.Expr)(nil)) | |
| walk(f, func(n interface{}) { | |
| if n == nil { | |
| return | |
| } | |
| v := reflect.ValueOf(n) | |
| if v.Type().Kind() != reflect.Ptr { | |
| return | |
| } | |
| if v.IsNil() { | |
| return | |
| } | |
| v = v.Elem() | |
| if v.Type().Kind() != reflect.Struct { | |
| return | |
| } | |
| for i := 0; i < v.NumField(); i++ { | |
| f := v.Field(i) | |
| if f.Type() == exprType { | |
| if r := badNils[f.Interface()]; r != nil { | |
| f.Set(reflect.ValueOf(r)) | |
| changed = true | |
| } | |
| } | |
| if f.Type() == exprSliceType { | |
| for j := 0; j < f.Len(); j++ { | |
| e := f.Index(j) | |
| if r := badNils[e.Interface()]; r != nil { | |
| e.Set(reflect.ValueOf(r)) | |
| changed = true | |
| } | |
| } | |
| } | |
| } | |
| }) | |
| } | |
| // step 3: fix up invalid casts. | |
| // It used to be ok to cast between *unsafe.Pointer and *C.CFTypeRef in a single step. | |
| // Now we need unsafe.Pointer as an intermediate cast. | |
| // (*unsafe.Pointer)(x) where x is type *bad -> (*unsafe.Pointer)(unsafe.Pointer(x)) | |
| // (*bad.type)(x) where x is type *unsafe.Pointer -> (*bad.type)(unsafe.Pointer(x)) | |
| walk(f, func(n interface{}) { | |
| if n == nil { | |
| return | |
| } | |
| // Find pattern like (*a.b)(x) | |
| c, ok := n.(*ast.CallExpr) | |
| if !ok { | |
| return | |
| } | |
| if len(c.Args) != 1 { | |
| return | |
| } | |
| p, ok := c.Fun.(*ast.ParenExpr) | |
| if !ok { | |
| return | |
| } | |
| s, ok := p.X.(*ast.StarExpr) | |
| if !ok { | |
| return | |
| } | |
| t, ok := s.X.(*ast.SelectorExpr) | |
| if !ok { | |
| return | |
| } | |
| pkg, ok := t.X.(*ast.Ident) | |
| if !ok { | |
| return | |
| } | |
| dst := pkg.Name + "." + t.Sel.Name | |
| src := typeof[c.Args[0]] | |
| if badType(dst) && src == "*unsafe.Pointer" || | |
| dst == "unsafe.Pointer" && strings.HasPrefix(src, "*") && badType(src[1:]) { | |
| c.Args[0] = &ast.CallExpr{ | |
| Fun: &ast.SelectorExpr{X: &ast.Ident{Name: "unsafe"}, Sel: &ast.Ident{Name: "Pointer"}}, | |
| Args: []ast.Expr{c.Args[0]}, | |
| } | |
| changed = true | |
| } | |
| }) | |
| return changed | |
| } |