| // 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[any]ast.Expr{} |
| walk(f, func(n any) { |
| 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.TypeFor[ast.Expr]() |
| exprSliceType := reflect.TypeFor[[]ast.Expr]() |
| walk(f, func(n any) { |
| if n == nil { |
| return |
| } |
| v := reflect.ValueOf(n) |
| if v.Type().Kind() != reflect.Pointer { |
| 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 any) { |
| 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 |
| } |